Other features

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

@ -13,6 +13,10 @@ type KeyMap struct {
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
@ -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"),

@ -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,
}
@ -117,6 +135,7 @@ type model struct {
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
@ -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()] {
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)
}
}
}

@ -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])
}
}

@ -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…
Cancel
Save