Other features

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

@ -3,24 +3,28 @@ package main
import "github.com/charmbracelet/bubbles/key" import "github.com/charmbracelet/bubbles/key"
type KeyMap struct { type KeyMap struct {
Quit key.Binding Quit key.Binding
Help key.Binding Help key.Binding
PageDown key.Binding PageDown key.Binding
PageUp key.Binding PageUp key.Binding
HalfPageUp key.Binding HalfPageUp key.Binding
HalfPageDown key.Binding HalfPageDown key.Binding
Down key.Binding Down key.Binding
Up key.Binding Up key.Binding
Expand key.Binding Expand key.Binding
Collapse key.Binding Collapse key.Binding
GotoTop key.Binding NextSibling key.Binding
GotoBottom key.Binding PrevSibling key.Binding
ToggleWrap key.Binding ExpandRecursively key.Binding
ExpandAll key.Binding CollapseRecursively key.Binding
CollapseAll key.Binding GotoTop key.Binding
Search key.Binding GotoBottom key.Binding
Next key.Binding ToggleWrap key.Binding
Prev key.Binding ExpandAll key.Binding
CollapseAll key.Binding
Search key.Binding
Next key.Binding
Prev key.Binding
} }
func DefaultKeyMap() KeyMap { func DefaultKeyMap() KeyMap {
@ -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,
} }
@ -112,12 +130,13 @@ type model struct {
keyMap KeyMap keyMap KeyMap
showHelp bool showHelp bool
expandedPaths map[string]bool // a set with expanded paths 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) canBeExpanded map[string]bool // a set for path => can be expanded (i.e. dict or array)
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
cursor int // cursor in range of m.pathToLineNumber.keys slice children map[string][]string // map of path => child paths
cursor int // cursor in range of m.pathToLineNumber.keys slice
showCursor bool showCursor bool
wrap bool wrap 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()] {
m.expandedPaths[m.cursorPath()] = false if key.Matches(msg, m.keyMap.CollapseRecursively) {
m.collapseRecursively(m.cursorPath())
} else {
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