Other features

sleep-stdin-bug
Anton Medvedev 2 years ago
parent fa226f2ae8
commit afd7b96af5

@ -13,6 +13,10 @@ type KeyMap struct {
Up key.Binding Up key.Binding
Expand key.Binding Expand key.Binding
Collapse key.Binding Collapse key.Binding
NextSibling key.Binding
PrevSibling key.Binding
ExpandRecursively key.Binding
CollapseRecursively key.Binding
GotoTop key.Binding GotoTop key.Binding
GotoBottom key.Binding GotoBottom key.Binding
ToggleWrap key.Binding ToggleWrap key.Binding
@ -65,6 +69,22 @@ func DefaultKeyMap() KeyMap {
key.WithKeys("left", "h"), key.WithKeys("left", "h"),
key.WithHelp("", "collapse"), 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( GotoTop: key.NewBinding(
key.WithKeys("g"), key.WithKeys("g"),
key.WithHelp("", "goto top"), key.WithHelp("", "goto top"),

@ -39,6 +39,7 @@ var colors = struct {
func main() { func main() {
filePath := "" filePath := ""
args := []string{}
var dec *json.Decoder var dec *json.Decoder
if term.IsTerminal(int(os.Stdin.Fd())) { if term.IsTerminal(int(os.Stdin.Fd())) {
if len(os.Args) >= 2 { if len(os.Args) >= 2 {
@ -48,9 +49,11 @@ func main() {
panic(err) panic(err)
} }
dec = json.NewDecoder(f) dec = json.NewDecoder(f)
args = os.Args[2:]
} }
} else { } else {
dec = json.NewDecoder(os.Stdin) dec = json.NewDecoder(os.Stdin)
args = os.Args[1:]
} }
dec.UseNumber() dec.UseNumber()
jsonObject, err := parse(dec) jsonObject, err := parse(dec)
@ -58,24 +61,37 @@ func main() {
panic(err) 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{ expand := map[string]bool{
"": true, "": true,
} }
if array, ok := jsonObject.(array); ok { if array, ok := jsonObject.(array); ok {
for i := range array { for i := range array {
expand[accessor("", i)] = true expand[accessor("", i)] = true
} }
} }
parents := map[string]string{} parents := map[string]string{}
children := map[string][]string{}
canBeExpanded := map[string]bool{}
dfs(jsonObject, func(it iterator) { dfs(jsonObject, func(it iterator) {
parents[it.path] = it.parent 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 := textinput.New()
input.Prompt = "" input.Prompt = ""
m := &model{ m := &model{
fileName: path.Base(filePath), fileName: path.Base(filePath),
json: jsonObject, json: jsonObject,
@ -84,7 +100,9 @@ func main() {
mouseWheelDelta: 3, mouseWheelDelta: 3,
keyMap: DefaultKeyMap(), keyMap: DefaultKeyMap(),
expandedPaths: expand, expandedPaths: expand,
canBeExpanded: canBeExpanded,
parents: parents, parents: parents,
children: children,
wrap: true, wrap: true,
searchInput: input, searchInput: input,
} }
@ -117,6 +135,7 @@ type model struct {
pathToLineNumber *dict // dict with path => line number pathToLineNumber *dict // dict with path => line number
lineNumberToPath map[int]string // map of line number => path lineNumberToPath map[int]string // map of line number => path
parents map[string]string // map of subpath => parent 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 cursor int // cursor in range of m.pathToLineNumber.keys slice
showCursor bool showCursor bool
@ -234,6 +253,18 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.SetOffset(at) 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): case key.Matches(msg, m.keyMap.Up):
m.showCursor = true m.showCursor = true
if m.cursor > 0 { 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.expandedPaths[m.cursorPath()] = true
m.render() 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 m.showCursor = true
if m.expandedPaths[m.cursorPath()] { if m.expandedPaths[m.cursorPath()] {
if key.Matches(msg, m.keyMap.CollapseRecursively) {
m.collapseRecursively(m.cursorPath())
} else {
m.expandedPaths[m.cursorPath()] = false m.expandedPaths[m.cursorPath()] = false
}
} else { } else {
parentPath, ok := m.parents[m.cursorPath()] parentPath, ok := m.parents[m.cursorPath()]
if ok { 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) index, _ := m.pathToLineNumber.index(parentPath)
m.cursor = index m.cursor = index
} }
@ -382,7 +426,6 @@ func (m *model) render() {
} }
m.pathToLineNumber = newDict() m.pathToLineNumber = newDict()
m.canBeExpanded = map[string]bool{}
m.lineNumberToPath = map[int]string{} m.lineNumberToPath = map[int]string{}
m.lines = m.print(m.json, 1, 0, 0, "", false) m.lines = m.print(m.json, 1, 0, 0, "", false)
@ -407,3 +450,23 @@ func (m *model) cursorLineNumber() int {
} }
return -1 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)
}
}
}

@ -7,6 +7,68 @@ import (
"strings" "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 { func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path string, dontHighlightCursor bool) []string {
ident := strings.Repeat(" ", level) ident := strings.Repeat(" ", level)
subident := strings.Repeat(" ", level-1) subident := strings.Repeat(" ", level-1)
@ -50,7 +112,6 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri
case *dict: case *dict:
m.pathToLineNumber.set(path, lineNumber) m.pathToLineNumber.set(path, lineNumber)
m.canBeExpanded[path] = true
m.lineNumberToPath[lineNumber] = path m.lineNumberToPath[lineNumber] = path
bracketStyle := cursorOr(colors.bracket).Render bracketStyle := cursorOr(colors.bracket).Render
if len(v.(*dict).keys) == 0 { if len(v.(*dict).keys) == 0 {
@ -102,7 +163,6 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri
case array: case array:
m.pathToLineNumber.set(path, lineNumber) m.pathToLineNumber.set(path, lineNumber)
m.canBeExpanded[path] = true
m.lineNumberToPath[lineNumber] = path m.lineNumberToPath[lineNumber] = path
bracketStyle := cursorOr(colors.bracket).Render bracketStyle := cursorOr(colors.bracket).Render
if len(v.(array)) == 0 { if len(v.(array)) == 0 {

@ -86,16 +86,16 @@ func (m *model) jumpToSearchResult(at int) {
m.SetOffset(lineNumber.(int)) m.SetOffset(lineNumber.(int))
m.render() m.render()
} else { } else {
m.recursiveExpand(desiredPath) m.expandToPath(desiredPath)
m.render() m.render()
m.jumpToSearchResult(at) m.jumpToSearchResult(at)
} }
} }
func (m *model) recursiveExpand(path string) { func (m *model) expandToPath(path string) {
m.expandedPaths[path] = true m.expandedPaths[path] = true
if path != "" { if path != "" {
m.recursiveExpand(m.parents[path]) m.expandToPath(m.parents[path])
} }
} }

@ -27,12 +27,49 @@ func max(a, b int) int {
return b return b
} }
func stringify(x interface{}) string { func stringify(v interface{}) string {
str, err := json.Marshal(x) switch v.(type) {
if err != nil { case nil:
panic(err) 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 { func width(s string) int {

Loading…
Cancel
Save