mirror of
https://github.com/antonmedv/fx
synced 2024-11-03 15:40:12 +00:00
Other features
This commit is contained in:
parent
fa226f2ae8
commit
afd7b96af5
56
keymap.go
56
keymap.go
@ -3,24 +3,28 @@ package main
|
||||
import "github.com/charmbracelet/bubbles/key"
|
||||
|
||||
type KeyMap struct {
|
||||
Quit key.Binding
|
||||
Help key.Binding
|
||||
PageDown key.Binding
|
||||
PageUp key.Binding
|
||||
HalfPageUp key.Binding
|
||||
HalfPageDown key.Binding
|
||||
Down key.Binding
|
||||
Up key.Binding
|
||||
Expand key.Binding
|
||||
Collapse key.Binding
|
||||
GotoTop key.Binding
|
||||
GotoBottom key.Binding
|
||||
ToggleWrap key.Binding
|
||||
ExpandAll key.Binding
|
||||
CollapseAll key.Binding
|
||||
Search key.Binding
|
||||
Next key.Binding
|
||||
Prev key.Binding
|
||||
Quit key.Binding
|
||||
Help key.Binding
|
||||
PageDown key.Binding
|
||||
PageUp key.Binding
|
||||
HalfPageUp key.Binding
|
||||
HalfPageDown key.Binding
|
||||
Down key.Binding
|
||||
Up key.Binding
|
||||
Expand key.Binding
|
||||
Collapse key.Binding
|
||||
NextSibling key.Binding
|
||||
PrevSibling key.Binding
|
||||
ExpandRecursively key.Binding
|
||||
CollapseRecursively key.Binding
|
||||
GotoTop key.Binding
|
||||
GotoBottom key.Binding
|
||||
ToggleWrap key.Binding
|
||||
ExpandAll key.Binding
|
||||
CollapseAll key.Binding
|
||||
Search key.Binding
|
||||
Next key.Binding
|
||||
Prev key.Binding
|
||||
}
|
||||
|
||||
func DefaultKeyMap() KeyMap {
|
||||
@ -65,6 +69,22 @@ func DefaultKeyMap() KeyMap {
|
||||
key.WithKeys("left", "h"),
|
||||
key.WithHelp("", "collapse"),
|
||||
),
|
||||
NextSibling: key.NewBinding(
|
||||
key.WithKeys("J"),
|
||||
key.WithHelp("", "next sibling"),
|
||||
),
|
||||
PrevSibling: key.NewBinding(
|
||||
key.WithKeys("K"),
|
||||
key.WithHelp("", "previous sibling"),
|
||||
),
|
||||
ExpandRecursively: key.NewBinding(
|
||||
key.WithKeys("L"),
|
||||
key.WithHelp("", "expand recursively"),
|
||||
),
|
||||
CollapseRecursively: key.NewBinding(
|
||||
key.WithKeys("H"),
|
||||
key.WithHelp("", "collapse recursively"),
|
||||
),
|
||||
GotoTop: key.NewBinding(
|
||||
key.WithKeys("g"),
|
||||
key.WithHelp("", "goto top"),
|
||||
|
91
main.go
91
main.go
@ -39,6 +39,7 @@ var colors = struct {
|
||||
|
||||
func main() {
|
||||
filePath := ""
|
||||
args := []string{}
|
||||
var dec *json.Decoder
|
||||
if term.IsTerminal(int(os.Stdin.Fd())) {
|
||||
if len(os.Args) >= 2 {
|
||||
@ -48,9 +49,11 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
dec = json.NewDecoder(f)
|
||||
args = os.Args[2:]
|
||||
}
|
||||
} else {
|
||||
dec = json.NewDecoder(os.Stdin)
|
||||
args = os.Args[1:]
|
||||
}
|
||||
dec.UseNumber()
|
||||
jsonObject, err := parse(dec)
|
||||
@ -58,24 +61,37 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if len(args) > 0 {
|
||||
if args[0] == "--print-code" {
|
||||
fmt.Print(generateCode(args[1:]))
|
||||
return
|
||||
}
|
||||
reduce(jsonObject, args)
|
||||
return
|
||||
}
|
||||
|
||||
expand := map[string]bool{
|
||||
"": true,
|
||||
}
|
||||
|
||||
if array, ok := jsonObject.(array); ok {
|
||||
for i := range array {
|
||||
expand[accessor("", i)] = true
|
||||
}
|
||||
}
|
||||
|
||||
parents := map[string]string{}
|
||||
children := map[string][]string{}
|
||||
canBeExpanded := map[string]bool{}
|
||||
dfs(jsonObject, func(it iterator) {
|
||||
parents[it.path] = it.parent
|
||||
})
|
||||
children[it.parent] = append(children[it.parent], it.path)
|
||||
switch it.object.(type) {
|
||||
case *dict, array:
|
||||
canBeExpanded[it.path] = true
|
||||
|
||||
}
|
||||
})
|
||||
input := textinput.New()
|
||||
input.Prompt = ""
|
||||
|
||||
m := &model{
|
||||
fileName: path.Base(filePath),
|
||||
json: jsonObject,
|
||||
@ -84,7 +100,9 @@ func main() {
|
||||
mouseWheelDelta: 3,
|
||||
keyMap: DefaultKeyMap(),
|
||||
expandedPaths: expand,
|
||||
canBeExpanded: canBeExpanded,
|
||||
parents: parents,
|
||||
children: children,
|
||||
wrap: true,
|
||||
searchInput: input,
|
||||
}
|
||||
@ -112,12 +130,13 @@ type model struct {
|
||||
keyMap KeyMap
|
||||
showHelp bool
|
||||
|
||||
expandedPaths map[string]bool // a set with expanded paths
|
||||
canBeExpanded map[string]bool // a set for path => can be expanded (i.e. dict or array)
|
||||
pathToLineNumber *dict // dict with path => line number
|
||||
lineNumberToPath map[int]string // map of line number => path
|
||||
parents map[string]string // map of subpath => parent path
|
||||
cursor int // cursor in range of m.pathToLineNumber.keys slice
|
||||
expandedPaths map[string]bool // a set with expanded paths
|
||||
canBeExpanded map[string]bool // a set for path => can be expanded (i.e. dict or array)
|
||||
pathToLineNumber *dict // dict with path => line number
|
||||
lineNumberToPath map[int]string // map of line number => path
|
||||
parents map[string]string // map of subpath => parent path
|
||||
children map[string][]string // map of path => child paths
|
||||
cursor int // cursor in range of m.pathToLineNumber.keys slice
|
||||
showCursor bool
|
||||
|
||||
wrap bool
|
||||
@ -234,6 +253,18 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.SetOffset(at)
|
||||
}
|
||||
|
||||
case key.Matches(msg, m.keyMap.NextSibling):
|
||||
m.showCursor = true
|
||||
// TODO: write code for collecting siblings,
|
||||
// and write code to getting sibling and jumping to it.
|
||||
m.render()
|
||||
at := m.cursorLineNumber()
|
||||
if m.offset <= at { // cursor is lower
|
||||
m.LineDown(max(0, at-(m.offset+m.height-1))) // minus one is due to cursorLineNumber() starts from 0
|
||||
} else {
|
||||
m.SetOffset(at)
|
||||
}
|
||||
|
||||
case key.Matches(msg, m.keyMap.Up):
|
||||
m.showCursor = true
|
||||
if m.cursor > 0 {
|
||||
@ -255,14 +286,27 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.expandedPaths[m.cursorPath()] = true
|
||||
m.render()
|
||||
|
||||
case key.Matches(msg, m.keyMap.Collapse):
|
||||
case key.Matches(msg, m.keyMap.ExpandRecursively):
|
||||
m.showCursor = true
|
||||
m.expandRecursively(m.cursorPath())
|
||||
m.render()
|
||||
|
||||
case key.Matches(msg, m.keyMap.Collapse, m.keyMap.CollapseRecursively):
|
||||
m.showCursor = true
|
||||
if m.expandedPaths[m.cursorPath()] {
|
||||
m.expandedPaths[m.cursorPath()] = false
|
||||
if key.Matches(msg, m.keyMap.CollapseRecursively) {
|
||||
m.collapseRecursively(m.cursorPath())
|
||||
} else {
|
||||
m.expandedPaths[m.cursorPath()] = false
|
||||
}
|
||||
} else {
|
||||
parentPath, ok := m.parents[m.cursorPath()]
|
||||
if ok {
|
||||
m.expandedPaths[parentPath] = false
|
||||
if key.Matches(msg, m.keyMap.CollapseRecursively) {
|
||||
m.collapseRecursively(m.cursorPath())
|
||||
} else {
|
||||
m.expandedPaths[m.cursorPath()] = false
|
||||
}
|
||||
index, _ := m.pathToLineNumber.index(parentPath)
|
||||
m.cursor = index
|
||||
}
|
||||
@ -382,7 +426,6 @@ func (m *model) render() {
|
||||
}
|
||||
|
||||
m.pathToLineNumber = newDict()
|
||||
m.canBeExpanded = map[string]bool{}
|
||||
m.lineNumberToPath = map[int]string{}
|
||||
m.lines = m.print(m.json, 1, 0, 0, "", false)
|
||||
|
||||
@ -407,3 +450,23 @@ func (m *model) cursorLineNumber() int {
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func (m *model) expandRecursively(path string) {
|
||||
if m.canBeExpanded[path] {
|
||||
m.expandedPaths[path] = true
|
||||
for _, childPath := range m.children[path] {
|
||||
if childPath != "" {
|
||||
m.expandRecursively(childPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) collapseRecursively(path string) {
|
||||
m.expandedPaths[path] = false
|
||||
for _, childPath := range m.children[path] {
|
||||
if childPath != "" {
|
||||
m.collapseRecursively(childPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
print.go
64
print.go
@ -7,6 +7,68 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func prettyPrint(v interface{}, level int) string {
|
||||
ident := strings.Repeat(" ", level)
|
||||
subident := strings.Repeat(" ", level-1)
|
||||
switch v.(type) {
|
||||
case nil:
|
||||
return colors.null.Render("null")
|
||||
|
||||
case bool:
|
||||
if v.(bool) {
|
||||
return colors.boolean.Render("true")
|
||||
} else {
|
||||
return colors.boolean.Render("false")
|
||||
}
|
||||
|
||||
case json.Number:
|
||||
return colors.number.Render(v.(json.Number).String())
|
||||
|
||||
case string:
|
||||
return colors.string.Render(fmt.Sprintf("%q", v))
|
||||
|
||||
case *dict:
|
||||
keys := v.(*dict).keys
|
||||
if len(keys) == 0 {
|
||||
return colors.bracket.Render("{}")
|
||||
}
|
||||
output := colors.bracket.Render("{\n")
|
||||
for i, k := range keys {
|
||||
key := colors.key.Render(fmt.Sprintf("%q", k))
|
||||
value, _ := v.(*dict).get(k)
|
||||
delim := ": "
|
||||
line := ident + key + delim + prettyPrint(value, level+1)
|
||||
if i < len(keys)-1 {
|
||||
line += ",\n"
|
||||
} else {
|
||||
line += "\n"
|
||||
}
|
||||
output += line
|
||||
}
|
||||
return output + subident + colors.bracket.Render("}")
|
||||
|
||||
case array:
|
||||
slice := v.(array)
|
||||
if len(slice) == 0 {
|
||||
return colors.bracket.Render("[]")
|
||||
}
|
||||
output := colors.bracket.Render("[\n")
|
||||
for i, value := range v.(array) {
|
||||
line := ident + prettyPrint(value, level+1)
|
||||
if i < len(slice)-1 {
|
||||
line += ",\n"
|
||||
} else {
|
||||
line += "\n"
|
||||
}
|
||||
output += line
|
||||
}
|
||||
return output + subident + colors.bracket.Render("]")
|
||||
|
||||
default:
|
||||
return "unknown type"
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path string, dontHighlightCursor bool) []string {
|
||||
ident := strings.Repeat(" ", level)
|
||||
subident := strings.Repeat(" ", level-1)
|
||||
@ -50,7 +112,6 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri
|
||||
|
||||
case *dict:
|
||||
m.pathToLineNumber.set(path, lineNumber)
|
||||
m.canBeExpanded[path] = true
|
||||
m.lineNumberToPath[lineNumber] = path
|
||||
bracketStyle := cursorOr(colors.bracket).Render
|
||||
if len(v.(*dict).keys) == 0 {
|
||||
@ -102,7 +163,6 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri
|
||||
|
||||
case array:
|
||||
m.pathToLineNumber.set(path, lineNumber)
|
||||
m.canBeExpanded[path] = true
|
||||
m.lineNumberToPath[lineNumber] = path
|
||||
bracketStyle := cursorOr(colors.bracket).Render
|
||||
if len(v.(array)) == 0 {
|
||||
|
@ -86,16 +86,16 @@ func (m *model) jumpToSearchResult(at int) {
|
||||
m.SetOffset(lineNumber.(int))
|
||||
m.render()
|
||||
} else {
|
||||
m.recursiveExpand(desiredPath)
|
||||
m.expandToPath(desiredPath)
|
||||
m.render()
|
||||
m.jumpToSearchResult(at)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *model) recursiveExpand(path string) {
|
||||
func (m *model) expandToPath(path string) {
|
||||
m.expandedPaths[path] = true
|
||||
if path != "" {
|
||||
m.recursiveExpand(m.parents[path])
|
||||
m.expandToPath(m.parents[path])
|
||||
}
|
||||
}
|
||||
|
||||
|
47
util.go
47
util.go
@ -27,12 +27,49 @@ func max(a, b int) int {
|
||||
return b
|
||||
}
|
||||
|
||||
func stringify(x interface{}) string {
|
||||
str, err := json.Marshal(x)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
func stringify(v interface{}) string {
|
||||
switch v.(type) {
|
||||
case nil:
|
||||
return "null"
|
||||
|
||||
case bool:
|
||||
if v.(bool) {
|
||||
return "true"
|
||||
} else {
|
||||
return "false"
|
||||
}
|
||||
|
||||
case json.Number:
|
||||
return v.(json.Number).String()
|
||||
|
||||
case string:
|
||||
return fmt.Sprintf("%q", v)
|
||||
|
||||
case *dict:
|
||||
result := "{"
|
||||
for i, key := range v.(*dict).keys {
|
||||
line := fmt.Sprintf("%q", key) + ":" + stringify(v.(*dict).values[key])
|
||||
if i < len(v.(*dict).keys)-1 {
|
||||
line += ","
|
||||
}
|
||||
result += line
|
||||
}
|
||||
return result + "}"
|
||||
|
||||
case array:
|
||||
result := "["
|
||||
for i, value := range v.(array) {
|
||||
line := stringify(value)
|
||||
if i < len(v.(array))-1 {
|
||||
line += ","
|
||||
}
|
||||
result += line
|
||||
}
|
||||
return result + "]"
|
||||
|
||||
default:
|
||||
return "unknown type"
|
||||
}
|
||||
return string(str)
|
||||
}
|
||||
|
||||
func width(s string) int {
|
||||
|
Loading…
Reference in New Issue
Block a user