From bf166fafda06999df4dd5126f143321d1af4330c Mon Sep 17 00:00:00 2001 From: Anton Medvedev Date: Sun, 17 Apr 2022 22:57:12 +0200 Subject: [PATCH] Move part of the code to pkg --- dict.go | 26 --- main.go | 66 ++++--- parse_test.go | 50 ----- pkg/dict/dict.go | 26 +++ dict_test.go => pkg/dict/dict_test.go | 28 +-- parse.go => pkg/json/parse.go | 15 +- pkg/json/parse_test.go | 51 +++++ pkg/json/pretty_print.go | 71 +++++++ stringify.go => pkg/json/stringify.go | 25 +-- .../json/stringify_test.go | 23 ++- pkg/json/traverse.go | 47 +++++ pkg/json/types.go | 6 + reduce.go => pkg/reducer/reduce.go | 84 ++------ reduce.js => pkg/reducer/reduce.js | 0 pkg/theme/theme.go | 185 ++++++++++++++++++ print.go | 81 ++++---- search.go | 22 ++- search_test.go | 28 +-- theme.go | 185 ------------------ traverse.go | 38 ---- viewport.go | 8 +- 21 files changed, 552 insertions(+), 513 deletions(-) delete mode 100644 dict.go delete mode 100644 parse_test.go create mode 100644 pkg/dict/dict.go rename dict_test.go => pkg/dict/dict_test.go (74%) rename parse.go => pkg/json/parse.go (86%) create mode 100644 pkg/json/parse_test.go create mode 100644 pkg/json/pretty_print.go rename stringify.go => pkg/json/stringify.go (51%) rename stringify_test.go => pkg/json/stringify_test.go (53%) create mode 100644 pkg/json/traverse.go create mode 100644 pkg/json/types.go rename reduce.go => pkg/reducer/reduce.go (56%) rename reduce.js => pkg/reducer/reduce.js (100%) create mode 100644 pkg/theme/theme.go delete mode 100644 theme.go delete mode 100644 traverse.go diff --git a/dict.go b/dict.go deleted file mode 100644 index 5477eb5..0000000 --- a/dict.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -type dict struct { - keys []string - values map[string]interface{} -} - -func newDict() *dict { - return &dict{ - keys: make([]string, 0), - values: make(map[string]interface{}), - } -} - -func (d *dict) get(key string) (interface{}, bool) { - val, exists := d.values[key] - return val, exists -} - -func (d *dict) set(key string, value interface{}) { - _, exists := d.values[key] - if !exists { - d.keys = append(d.keys, key) - } - d.values[key] = value -} diff --git a/main.go b/main.go index c9a854a..c24a1a4 100644 --- a/main.go +++ b/main.go @@ -3,21 +3,23 @@ package main import ( "encoding/json" "fmt" + . "github.com/antonmedv/fx/pkg/dict" + . "github.com/antonmedv/fx/pkg/json" + "github.com/antonmedv/fx/pkg/reducer" + . "github.com/antonmedv/fx/pkg/theme" "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/mattn/go-isatty" "github.com/muesli/termenv" "golang.org/x/term" + "io/fs" "os" "path" - "io/fs" "runtime/pprof" "strings" ) -type number = json.Number - func main() { cpuProfile := os.Getenv("CPU_PROFILE") if cpuProfile != "" { @@ -35,12 +37,12 @@ func main() { if !ok { themeId = "1" } - theme, ok := themes[themeId] + theme, ok := Themes[themeId] if !ok { - theme = themes["1"] + theme = Themes["1"] } if termenv.ColorProfile() == termenv.Ascii { - theme = themes["0"] + theme = Themes["0"] } filePath := "" @@ -71,7 +73,7 @@ func main() { os.Exit(1) } dec.UseNumber() - jsonObject, err := parse(dec) + jsonObject, err := Parse(dec) if err != nil { panic(err) } @@ -79,17 +81,17 @@ func main() { tty := isatty.IsTerminal(os.Stdout.Fd()) if len(args) > 0 || !tty { if len(args) > 0 && args[0] == "--print-code" { - fmt.Print(generateCode(args[1:])) + fmt.Print(reducer.GenerateCode(args[1:])) return } - reduce(jsonObject, args, theme) + reducer.Reduce(jsonObject, args, theme) return } expand := map[string]bool{ "": true, } - if array, ok := jsonObject.(array); ok { + if array, ok := jsonObject.(Array); ok { for i := range array { expand[accessor("", i)] = true } @@ -97,14 +99,14 @@ func main() { 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: - canBeExpanded[it.path] = len(it.object.(*dict).keys) > 0 - case array: - canBeExpanded[it.path] = len(it.object.(array)) > 0 + 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: + canBeExpanded[it.Path] = len(it.Object.(*Dict).Keys) > 0 + case Array: + canBeExpanded[it.Path] = len(it.Object.(Array)) > 0 } }) @@ -152,7 +154,7 @@ type model struct { json interface{} lines []string - mouseWheelDelta int // number of lines the mouse wheel will scroll + mouseWheelDelta int // Number of lines the mouse wheel will scroll offset int // offset is the vertical scroll position keyMap KeyMap @@ -161,9 +163,9 @@ type model struct { expandedPaths map[string]bool // set of expanded paths canBeExpanded map[string]bool // set of path => can be expanded (i.e. dict or array) paths []string // array of paths on screen - pathToLineNumber map[string]int // map of path => line number + pathToLineNumber map[string]int // map of path => line Number pathToIndex map[string]int // map of path => index in m.paths - 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 children map[string][]string // map of path => child paths nextSiblings, prevSiblings map[string]string // map of path => sibling path @@ -342,10 +344,10 @@ func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.render() case key.Matches(msg, m.keyMap.ExpandAll): - dfs(m.json, func(it iterator) { - switch it.object.(type) { - case *dict, array: - m.expandedPaths[it.path] = true + Dfs(m.json, func(it Iterator) { + switch it.Object.(type) { + case *Dict, Array: + m.expandedPaths[it.Path] = true } }) m.render() @@ -400,13 +402,13 @@ func (m *model) View() string { if m.showHelp { statusBar := "Press Esc or q to close help." statusBar += strings.Repeat(" ", max(0, m.width-width(statusBar))) - statusBar = m.theme.statusBar(statusBar) + statusBar = m.theme.StatusBar(statusBar) return strings.Join(lines, "\n") + extraLines + "\n" + statusBar } statusBar := m.cursorPath() + " " statusBar += strings.Repeat(" ", max(0, m.width-width(statusBar)-width(m.fileName))) statusBar += m.fileName - statusBar = m.theme.statusBar(statusBar) + statusBar = m.theme.StatusBar(statusBar) output := strings.Join(lines, "\n") + extraLines + "\n" + statusBar if m.searchInput.Focused() { output += "\n/" + m.searchInput.View() @@ -506,22 +508,22 @@ func (m *model) collapseRecursively(path string) { func (m *model) collectSiblings(v interface{}, path string) { switch v.(type) { - case *dict: + case *Dict: prev := "" - for _, k := range v.(*dict).keys { + for _, k := range v.(*Dict).Keys { subpath := path + "." + k if prev != "" { m.nextSiblings[prev] = subpath m.prevSiblings[subpath] = prev } prev = subpath - value, _ := v.(*dict).get(k) + value, _ := v.(*Dict).Get(k) m.collectSiblings(value, subpath) } - case array: + case Array: prev := "" - for i, value := range v.(array) { + for i, value := range v.(Array) { subpath := fmt.Sprintf("%v[%v]", path, i) if prev != "" { m.nextSiblings[prev] = subpath diff --git a/parse_test.go b/parse_test.go deleted file mode 100644 index 07b28c1..0000000 --- a/parse_test.go +++ /dev/null @@ -1,50 +0,0 @@ -package main - -import ( - "encoding/json" - "strings" - "testing" -) - -func Test_parse(t *testing.T) { - input := `{ - "a": 1, - "b": 2, - "a": 3, - "slice": [{"z": "z", "1": "1"}] -}` - - p, err := parse(json.NewDecoder(strings.NewReader(input))) - if err != nil { - t.Error("JSON parse error", err) - } - o := p.(*dict) - - expectedKeys := []string{ - "a", - "b", - "slice", - } - for i := range o.keys { - if o.keys[i] != expectedKeys[i] { - t.Error("Wrong key order ", i, o.keys[i], "!=", expectedKeys[i]) - } - } - - s, ok := o.get("slice") - if !ok { - t.Error("slice missing") - } - a := s.(array) - z := a[0].(*dict) - - expectedKeys = []string{ - "z", - "1", - } - for i := range z.keys { - if z.keys[i] != expectedKeys[i] { - t.Error("Wrong key order for nested map ", i, z.keys[i], "!=", expectedKeys[i]) - } - } -} diff --git a/pkg/dict/dict.go b/pkg/dict/dict.go new file mode 100644 index 0000000..7b6ad3a --- /dev/null +++ b/pkg/dict/dict.go @@ -0,0 +1,26 @@ +package dict + +type Dict struct { + Keys []string + Values map[string]interface{} +} + +func NewDict() *Dict { + return &Dict{ + Keys: make([]string, 0), + Values: make(map[string]interface{}), + } +} + +func (d *Dict) Get(key string) (interface{}, bool) { + val, exists := d.Values[key] + return val, exists +} + +func (d *Dict) Set(key string, value interface{}) { + _, exists := d.Values[key] + if !exists { + d.Keys = append(d.Keys, key) + } + d.Values[key] = value +} diff --git a/dict_test.go b/pkg/dict/dict_test.go similarity index 74% rename from dict_test.go rename to pkg/dict/dict_test.go index 388cbd2..baa2caa 100644 --- a/dict_test.go +++ b/pkg/dict/dict_test.go @@ -1,26 +1,26 @@ -package main +package dict import "testing" func Test_dict(t *testing.T) { - d := newDict() - d.set("number", 3) - v, _ := d.get("number") + d := NewDict() + d.Set("number", 3) + v, _ := d.Get("number") if v.(int) != 3 { t.Error("Set number") } // string - d.set("string", "x") - v, _ = d.get("string") + d.Set("string", "x") + v, _ = d.Get("string") if v.(string) != "x" { t.Error("Set string") } // string slice - d.set("strings", []string{ + d.Set("strings", []string{ "t", "u", }) - v, _ = d.get("strings") + v, _ = d.Get("strings") if v.([]string)[0] != "t" { t.Error("Set strings first index") } @@ -28,11 +28,11 @@ func Test_dict(t *testing.T) { t.Error("Set strings second index") } // mixed slice - d.set("mixed", []interface{}{ + d.Set("mixed", []interface{}{ 1, "1", }) - v, _ = d.get("mixed") + v, _ = d.Get("mixed") if v.([]interface{})[0].(int) != 1 { t.Error("Set mixed int") } @@ -40,19 +40,19 @@ func Test_dict(t *testing.T) { t.Error("Set mixed string") } // overriding existing key - d.set("number", 4) - v, _ = d.get("number") + d.Set("number", 4) + v, _ = d.Get("number") if v.(int) != 4 { t.Error("Override existing key") } - // keys + // Keys expectedKeys := []string{ "number", "string", "strings", "mixed", } - for i, key := range d.keys { + for i, key := range d.Keys { if key != expectedKeys[i] { t.Error("Keys method", key, "!=", expectedKeys[i]) } diff --git a/parse.go b/pkg/json/parse.go similarity index 86% rename from parse.go rename to pkg/json/parse.go index e4613db..8fbc6f4 100644 --- a/parse.go +++ b/pkg/json/parse.go @@ -1,10 +1,11 @@ -package main +package json import ( "encoding/json" + . "github.com/antonmedv/fx/pkg/dict" ) -func parse(dec *json.Decoder) (interface{}, error) { +func Parse(dec *json.Decoder) (interface{}, error) { token, err := dec.Token() if err != nil { return nil, err @@ -20,8 +21,8 @@ func parse(dec *json.Decoder) (interface{}, error) { return token, nil } -func decodeDict(dec *json.Decoder) (*dict, error) { - d := newDict() +func decodeDict(dec *json.Decoder) (*Dict, error) { + d := NewDict() for { token, err := dec.Token() if err != nil { @@ -50,14 +51,12 @@ func decodeDict(dec *json.Decoder) (*dict, error) { } } } - d.set(key, value) + d.Set(key, value) } } -type array = []interface{} - func decodeArray(dec *json.Decoder) ([]interface{}, error) { - slice := make(array, 0) + slice := make(Array, 0) for index := 0; ; index++ { token, err := dec.Token() if err != nil { diff --git a/pkg/json/parse_test.go b/pkg/json/parse_test.go new file mode 100644 index 0000000..3942b7a --- /dev/null +++ b/pkg/json/parse_test.go @@ -0,0 +1,51 @@ +package json + +import ( + "encoding/json" + . "github.com/antonmedv/fx/pkg/dict" + "strings" + "testing" +) + +func Test_parse(t *testing.T) { + input := `{ + "a": 1, + "b": 2, + "a": 3, + "slice": [{"z": "z", "1": "1"}] +}` + + p, err := Parse(json.NewDecoder(strings.NewReader(input))) + if err != nil { + t.Error("JSON parse error", err) + } + o := p.(*Dict) + + expectedKeys := []string{ + "a", + "b", + "slice", + } + for i := range o.Keys { + if o.Keys[i] != expectedKeys[i] { + t.Error("Wrong key order ", i, o.Keys[i], "!=", expectedKeys[i]) + } + } + + s, ok := o.Get("slice") + if !ok { + t.Error("slice missing") + } + a := s.(Array) + z := a[0].(*Dict) + + expectedKeys = []string{ + "z", + "1", + } + for i := range z.Keys { + if z.Keys[i] != expectedKeys[i] { + t.Error("Wrong key order for nested map ", i, z.Keys[i], "!=", expectedKeys[i]) + } + } +} diff --git a/pkg/json/pretty_print.go b/pkg/json/pretty_print.go new file mode 100644 index 0000000..1b088d6 --- /dev/null +++ b/pkg/json/pretty_print.go @@ -0,0 +1,71 @@ +package json + +import ( + "encoding/json" + "fmt" + "github.com/antonmedv/fx/pkg/dict" + "github.com/antonmedv/fx/pkg/theme" + "strings" +) + +func PrettyPrint(v interface{}, level int, theme theme.Theme) string { + ident := strings.Repeat(" ", level) + subident := strings.Repeat(" ", level-1) + switch v.(type) { + case nil: + return theme.Null("null") + + case bool: + if v.(bool) { + return theme.Boolean("true") + } else { + return theme.Boolean("false") + } + + case json.Number: + return theme.Number(v.(json.Number).String()) + + case string: + return theme.String(fmt.Sprintf("%q", v)) + + case *dict.Dict: + keys := v.(*dict.Dict).Keys + if len(keys) == 0 { + return theme.Syntax("{}") + } + output := theme.Syntax("{") + output += "\n" + for i, k := range keys { + key := theme.Key(i, len(keys))(fmt.Sprintf("%q", k)) + value, _ := v.(*dict.Dict).Get(k) + delim := theme.Syntax(": ") + line := ident + key + delim + PrettyPrint(value, level+1, theme) + if i < len(keys)-1 { + line += theme.Syntax(",") + } + line += "\n" + output += line + } + return output + subident + theme.Syntax("}") + + case []interface{}: + slice := v.([]interface{}) + if len(slice) == 0 { + return theme.Syntax("[]") + } + output := theme.Syntax("[\n") + for i, value := range v.([]interface{}) { + line := ident + PrettyPrint(value, level+1, theme) + if i < len(slice)-1 { + line += ",\n" + } else { + line += "\n" + } + output += line + } + return output + subident + theme.Syntax("]") + + default: + return "unknown type" + } +} diff --git a/stringify.go b/pkg/json/stringify.go similarity index 51% rename from stringify.go rename to pkg/json/stringify.go index 0a93903..739cc71 100644 --- a/stringify.go +++ b/pkg/json/stringify.go @@ -1,10 +1,11 @@ -package main +package json import ( "fmt" + . "github.com/antonmedv/fx/pkg/dict" ) -func stringify(v interface{}) string { +func Stringify(v interface{}) string { switch v.(type) { case nil: return "null" @@ -16,28 +17,28 @@ func stringify(v interface{}) string { return "false" } - case number: - return v.(number).String() + case Number: + return v.(Number).String() case string: return fmt.Sprintf("%q", v) - case *dict: + 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 { + 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: + case Array: result := "[" - for i, value := range v.(array) { - line := stringify(value) - if i < len(v.(array))-1 { + for i, value := range v.(Array) { + line := Stringify(value) + if i < len(v.(Array))-1 { line += "," } result += line diff --git a/stringify_test.go b/pkg/json/stringify_test.go similarity index 53% rename from stringify_test.go rename to pkg/json/stringify_test.go index 8ac775a..fc9cc2f 100644 --- a/stringify_test.go +++ b/pkg/json/stringify_test.go @@ -1,28 +1,31 @@ -package main +package json -import "testing" +import ( + . "github.com/antonmedv/fx/pkg/dict" + "testing" +) func Test_stringify(t *testing.T) { t.Run("dict", func(t *testing.T) { - arg := newDict() - arg.set("a", number("1")) - arg.set("b", number("2")) + arg := NewDict() + arg.Set("a", Number("1")) + arg.Set("b", Number("2")) want := `{"a": 1,"b": 2}` - if got := stringify(arg); got != want { + if got := Stringify(arg); got != want { t.Errorf("stringify() = %v, want %v", got, want) } }) t.Run("array", func(t *testing.T) { - arg := array{number("1"), number("2")} + arg := Array{Number("1"), Number("2")} want := `[1,2]` - if got := stringify(arg); got != want { + if got := Stringify(arg); got != want { t.Errorf("stringify() = %v, want %v", got, want) } }) t.Run("array_with_dict", func(t *testing.T) { - arg := array{newDict(), array{}} + arg := Array{NewDict(), Array{}} want := `[{},[]]` - if got := stringify(arg); got != want { + if got := Stringify(arg); got != want { t.Errorf("stringify() = %v, want %v", got, want) } }) diff --git a/pkg/json/traverse.go b/pkg/json/traverse.go new file mode 100644 index 0000000..4253438 --- /dev/null +++ b/pkg/json/traverse.go @@ -0,0 +1,47 @@ +package json + +import ( + "fmt" + . "github.com/antonmedv/fx/pkg/dict" +) + +type Iterator struct { + Object interface{} + Path, Parent string +} + +func Dfs(object interface{}, f func(it Iterator)) { + sub(Iterator{Object: object}, f) +} + +func sub(it Iterator, f func(it Iterator)) { + f(it) + switch it.Object.(type) { + case *Dict: + keys := it.Object.(*Dict).Keys + for _, k := range keys { + subpath := it.Path + "." + k + value, _ := it.Object.(*Dict).Get(k) + sub(Iterator{ + Object: value, + Path: subpath, + Parent: it.Path, + }, f) + } + + case Array: + slice := it.Object.(Array) + for i, value := range slice { + subpath := accessor(it.Path, i) + sub(Iterator{ + Object: value, + Path: subpath, + Parent: it.Path, + }, f) + } + } +} + +func accessor(path string, to interface{}) string { + return fmt.Sprintf("%v[%v]", path, to) +} diff --git a/pkg/json/types.go b/pkg/json/types.go new file mode 100644 index 0000000..f34495e --- /dev/null +++ b/pkg/json/types.go @@ -0,0 +1,6 @@ +package json + +import "encoding/json" + +type Number = json.Number +type Array = []interface{} diff --git a/reduce.go b/pkg/reducer/reduce.go similarity index 56% rename from reduce.go rename to pkg/reducer/reduce.go index 46b3aec..f4f555f 100644 --- a/reduce.go +++ b/pkg/reducer/reduce.go @@ -1,10 +1,12 @@ -package main +package reducer import ( "bytes" _ "embed" "encoding/json" "fmt" + . "github.com/antonmedv/fx/pkg/json" + . "github.com/antonmedv/fx/pkg/theme" "os" "os/exec" "regexp" @@ -16,7 +18,7 @@ var template string var flatMapRegex = regexp.MustCompile("^(\\.\\w*)+\\[]") -func generateCode(args []string) string { +func GenerateCode(args []string) string { rs := "\n" for i, a := range args { rs += " try {" @@ -74,9 +76,9 @@ func generateCode(args []string) string { } pointer := fmt.Sprintf( "%v %v %v", - strings.Repeat(" ", width(pre)), - strings.Repeat("^", width(a)), - strings.Repeat(" ", width(post)), + strings.Repeat(" ", len(pre)), + strings.Repeat("^", len(a)), + strings.Repeat(" ", len(post)), ) rs += fmt.Sprintf( " throw `\\n"+ @@ -104,24 +106,24 @@ func fold(s []string) string { return fmt.Sprintf("x => Object.values(%v).flatMap(%v)", obj, fold(s[1:])) } -func reduce(object interface{}, args []string, theme Theme) { +func Reduce(object interface{}, args []string, theme Theme) { var stdout, stderr bytes.Buffer - cmd := exec.Command("node", "-e", generateCode(args)) + cmd := exec.Command("node", "-e", GenerateCode(args)) cmd.Env = os.Environ() cmd.Env = append(cmd.Env, "NODE_OPTIONS=--max-old-space-size=8192") - cmd.Stdin = strings.NewReader(stringify(object)) + cmd.Stdin = strings.NewReader(Stringify(object)) cmd.Stdout = &stdout cmd.Stderr = &stderr err := cmd.Run() if err == nil { dec := json.NewDecoder(&stdout) dec.UseNumber() - jsonObject, err := parse(dec) + jsonObject, err := Parse(dec) if err == nil { if str, ok := jsonObject.(string); ok { fmt.Println(str) } else { - fmt.Println(prettyPrint(jsonObject, 1, theme)) + fmt.Println(PrettyPrint(jsonObject, 1, theme)) } } else { _, _ = fmt.Fprint(os.Stderr, stderr.String()) @@ -136,65 +138,3 @@ func reduce(object interface{}, args []string, theme Theme) { os.Exit(exitCode) } } - -func prettyPrint(v interface{}, level int, theme Theme) string { - ident := strings.Repeat(" ", level) - subident := strings.Repeat(" ", level-1) - switch v.(type) { - case nil: - return theme.null("null") - - case bool: - if v.(bool) { - return theme.boolean("true") - } else { - return theme.boolean("false") - } - - case number: - return theme.number(v.(number).String()) - - case string: - return theme.string(fmt.Sprintf("%q", v)) - - case *dict: - keys := v.(*dict).keys - if len(keys) == 0 { - return theme.syntax("{}") - } - output := theme.syntax("{") - output += "\n" - for i, k := range keys { - key := theme.key(i, len(keys))(fmt.Sprintf("%q", k)) - value, _ := v.(*dict).get(k) - delim := theme.syntax(": ") - line := ident + key + delim + prettyPrint(value, level+1, theme) - if i < len(keys)-1 { - line += theme.syntax(",") - } - line += "\n" - output += line - } - return output + subident + theme.syntax("}") - - case array: - slice := v.(array) - if len(slice) == 0 { - return theme.syntax("[]") - } - output := theme.syntax("[\n") - for i, value := range v.(array) { - line := ident + prettyPrint(value, level+1, theme) - if i < len(slice)-1 { - line += ",\n" - } else { - line += "\n" - } - output += line - } - return output + subident + theme.syntax("]") - - default: - return "unknown type" - } -} diff --git a/reduce.js b/pkg/reducer/reduce.js similarity index 100% rename from reduce.js rename to pkg/reducer/reduce.js diff --git a/pkg/theme/theme.go b/pkg/theme/theme.go new file mode 100644 index 0000000..8197b75 --- /dev/null +++ b/pkg/theme/theme.go @@ -0,0 +1,185 @@ +package theme + +import ( + "github.com/charmbracelet/lipgloss" + "github.com/mazznoer/colorgrad" + "strings" +) + +type Theme struct { + Cursor Color + Syntax Color + Preview Color + StatusBar Color + Search Color + Key func(i, len int) Color + String Color + Null Color + Boolean Color + Number Color +} +type Color func(s string) string + +var ( + defaultCursor = lipgloss.NewStyle().Reverse(true).Render + defaultPreview = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("8")).Render + defaultStatusBar = lipgloss.NewStyle().Background(lipgloss.Color("7")).Foreground(lipgloss.Color("0")).Render + defaultSearch = lipgloss.NewStyle().Background(lipgloss.Color("11")).Foreground(lipgloss.Color("16")).Render + defaultNull = fg("8") +) + +var Themes = map[string]Theme{ + "0": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: noColor, + StatusBar: noColor, + Search: defaultSearch, + Key: func(_, _ int) Color { return noColor }, + String: noColor, + Null: noColor, + Boolean: noColor, + Number: noColor, + }, + "1": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return boldFg("4") }, + String: boldFg("2"), + Null: defaultNull, + Boolean: boldFg("3"), + Number: boldFg("6"), + }, + "2": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return fg("#00F5D4") }, + String: fg("#00BBF9"), + Null: defaultNull, + Boolean: fg("#F15BB5"), + Number: fg("#9B5DE5"), + }, + "3": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return fg("#faf0ca") }, + String: fg("#f4d35e"), + Null: defaultNull, + Boolean: fg("#ee964b"), + Number: fg("#ee964b"), + }, + "4": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return fg("#4D96FF") }, + String: fg("#6BCB77"), + Null: defaultNull, + Boolean: fg("#FF6B6B"), + Number: fg("#FFD93D"), + }, + "5": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return boldFg("42") }, + String: boldFg("213"), + Null: defaultNull, + Boolean: boldFg("201"), + Number: boldFg("201"), + }, + "6": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return gradient("rgb(125,110,221)", "rgb(90%,45%,97%)", "hsl(229,79%,85%)") }, + String: fg("195"), + Null: defaultNull, + Boolean: fg("195"), + Number: fg("195"), + }, + "7": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: func(_, _ int) Color { return gradient("rgb(123,216,96)", "rgb(255,255,255)") }, + String: noColor, + Null: defaultNull, + Boolean: noColor, + Number: noColor, + }, + "8": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: gradientKeys("#ff0000", "#ff8700", "#ffd300", "#deff0a", "#a1ff0a", "#0aff99", "#0aefff", "#147df5", "#580aff", "#be0aff"), + String: noColor, + Null: defaultNull, + Boolean: noColor, + Number: noColor, + }, + "9": { + Cursor: defaultCursor, + Syntax: noColor, + Preview: defaultPreview, + StatusBar: defaultStatusBar, + Search: defaultSearch, + Key: gradientKeys("rgb(34,126,34)", "rgb(168,251,60)"), + String: gradient("rgb(34,126,34)", "rgb(168,251,60)"), + Null: defaultNull, + Boolean: noColor, + Number: noColor, + }, +} + +func noColor(s string) string { + return s +} + +func fg(color string) Color { + return lipgloss.NewStyle().Foreground(lipgloss.Color(color)).Render +} + +func boldFg(color string) Color { + return lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color(color)).Render +} + +func gradient(colors ...string) Color { + grad, _ := colorgrad.NewGradient().HtmlColors(colors...).Build() + return func(s string) string { + runes := []rune(s) + colors := grad.ColorfulColors(uint(len(runes))) + var out strings.Builder + for i, r := range runes { + style := lipgloss.NewStyle().Foreground(lipgloss.Color(colors[i].Hex())) + out.WriteString(style.Render(string(r))) + } + return out.String() + } +} + +func gradientKeys(colors ...string) func(i, len int) Color { + grad, _ := colorgrad.NewGradient().HtmlColors(colors...).Build() + return func(i, len int) Color { + return lipgloss.NewStyle().Foreground(lipgloss.Color(grad.At(float64(i) / float64(len)).Hex())).Render + } +} diff --git a/print.go b/print.go index 1ae050e..49e680c 100644 --- a/print.go +++ b/print.go @@ -2,6 +2,9 @@ package main import ( "fmt" + . "github.com/antonmedv/fx/pkg/dict" + . "github.com/antonmedv/fx/pkg/json" + "github.com/antonmedv/fx/pkg/theme" "strings" ) @@ -27,34 +30,34 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri switch v.(type) { case nil: - return []string{merge(m.explode("null", searchValue, m.theme.null, path, selectableValues))} + return []string{merge(m.explode("null", searchValue, m.theme.Null, path, selectableValues))} case bool: if v.(bool) { - return []string{merge(m.explode("true", searchValue, m.theme.boolean, path, selectableValues))} + return []string{merge(m.explode("true", searchValue, m.theme.Boolean, path, selectableValues))} } else { - return []string{merge(m.explode("false", searchValue, m.theme.boolean, path, selectableValues))} + return []string{merge(m.explode("false", searchValue, m.theme.Boolean, path, selectableValues))} } - case number: - return []string{merge(m.explode(v.(number).String(), searchValue, m.theme.number, path, selectableValues))} + case Number: + return []string{merge(m.explode(v.(Number).String(), searchValue, m.theme.Number, path, selectableValues))} case string: line := fmt.Sprintf("%q", v) - chunks := m.explode(line, searchValue, m.theme.string, path, selectableValues) + chunks := m.explode(line, searchValue, m.theme.String, path, selectableValues) if m.wrap && keyEndPos+width(line) > m.width { return wrapLines(chunks, keyEndPos, m.width, subident) } // No wrap return []string{merge(chunks)} - case *dict: + case *Dict: if !m.expandedPaths[path] { return []string{m.preview(v, path, selectableValues)} } output := []string{m.printOpenBracket("{", highlight, path, selectableValues)} lineNumber++ // bracket is on separate line - keys := v.(*dict).keys + keys := v.(*Dict).Keys for i, k := range keys { subpath := path + "." + k highlight := m.highlightIndex[subpath] @@ -65,10 +68,10 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri } m.connect(subpath, lineNumber) key := fmt.Sprintf("%q", k) - keyTheme := m.theme.key(i, len(keys)) + keyTheme := m.theme.Key(i, len(keys)) key = merge(m.explode(key, keyRanges, keyTheme, subpath, true)) - value, _ := v.(*dict).get(k) - delim := merge(m.explode(": ", delimRanges, m.theme.syntax, subpath, false)) + value, _ := v.(*Dict).Get(k) + delim := merge(m.explode(": ", delimRanges, m.theme.Syntax, subpath, false)) keyEndPos := width(ident) + width(key) + width(delim) lines := m.print(value, level+1, lineNumber, keyEndPos, subpath, false) lines[0] = ident + key + delim + lines[0] @@ -81,13 +84,13 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri output = append(output, subident+m.printCloseBracket("}", highlight, path, false)) return output - case array: + case Array: if !m.expandedPaths[path] { return []string{m.preview(v, path, selectableValues)} } output := []string{m.printOpenBracket("[", highlight, path, selectableValues)} lineNumber++ // bracket is on separate line - slice := v.(array) + slice := v.(Array) for i, value := range slice { subpath := fmt.Sprintf("%v[%v]", path, i) s := m.highlightIndex[subpath] @@ -110,32 +113,32 @@ func (m *model) print(v interface{}, level, lineNumber, keyEndPos int, path stri func (m *model) preview(v interface{}, path string, selectableValues bool) string { searchResult := m.highlightIndex[path] - previewStyle := m.theme.preview + previewStyle := m.theme.Preview if selectableValues && m.cursorPath() == path { - previewStyle = m.theme.cursor + previewStyle = m.theme.Cursor } printValue := func(value interface{}) string { switch value.(type) { - case nil, bool, number: + case nil, bool, Number: return previewStyle(fmt.Sprintf("%v", value)) case string: return previewStyle(fmt.Sprintf("%q", value)) - case *dict: + case *Dict: return previewStyle("{\u2026}") - case array: + case Array: return previewStyle("[\u2026]") } return "..." } switch v.(type) { - case *dict: + case *Dict: output := m.printOpenBracket("{", searchResult, path, selectableValues) - keys := v.(*dict).keys + keys := v.(*Dict).Keys for _, k := range keys { key := fmt.Sprintf("%q", k) output += previewStyle(key + ": ") - value, _ := v.(*dict).get(k) + value, _ := v.(*Dict).Get(k) output += printValue(value) break } @@ -145,9 +148,9 @@ func (m *model) preview(v interface{}, path string, selectableValues bool) strin output += m.printCloseBracket("}", searchResult, path, selectableValues) return output - case array: + case Array: output := m.printOpenBracket("[", searchResult, path, selectableValues) - slice := v.(array) + slice := v.(Array) for _, value := range slice { output += printValue(value) break @@ -194,62 +197,62 @@ func (w withStyle) Render(s string) string { func (m *model) printOpenBracket(line string, s *rangeGroup, path string, selectableValues bool) string { if selectableValues && m.cursorPath() == path { - return m.theme.cursor(line) + return m.theme.Cursor(line) } if s != nil && s.openBracket != nil { if s.openBracket.parent.index == m.searchResultsCursor { - return m.theme.cursor(line) + return m.theme.Cursor(line) } else { - return m.theme.search(line) + return m.theme.Search(line) } } else { - return m.theme.syntax(line) + return m.theme.Syntax(line) } } func (m *model) printCloseBracket(line string, s *rangeGroup, path string, selectableValues bool) string { if selectableValues && m.cursorPath() == path { - return m.theme.cursor(line) + return m.theme.Cursor(line) } if s != nil && s.closeBracket != nil { if s.closeBracket.parent.index == m.searchResultsCursor { - return m.theme.cursor(line) + return m.theme.Cursor(line) } else { - return m.theme.search(line) + return m.theme.Search(line) } } else { - return m.theme.syntax(line) + return m.theme.Syntax(line) } } func (m *model) printComma(line string, s *rangeGroup) string { if s != nil && s.comma != nil { if s.comma.parent.index == m.searchResultsCursor { - return m.theme.cursor(line) + return m.theme.Cursor(line) } else { - return m.theme.search(line) + return m.theme.Search(line) } } else { - return m.theme.syntax(line) + return m.theme.Syntax(line) } } type withStyle struct { value string - style Color + style theme.Color } -func (m *model) explode(line string, highlightRanges []*foundRange, defaultStyle Color, path string, selectable bool) []withStyle { +func (m *model) explode(line string, highlightRanges []*foundRange, defaultStyle theme.Color, path string, selectable bool) []withStyle { if selectable && m.cursorPath() == path && m.showCursor { - return []withStyle{{line, m.theme.cursor}} + return []withStyle{{line, m.theme.Cursor}} } out := make([]withStyle, 0, 1) pos := 0 for _, r := range highlightRanges { - style := m.theme.search + style := m.theme.Search if r.parent.index == m.searchResultsCursor { - style = m.theme.cursor + style = m.theme.Cursor } out = append(out, withStyle{ value: line[pos:r.start], diff --git a/search.go b/search.go index 78e2fac..15272df 100644 --- a/search.go +++ b/search.go @@ -2,6 +2,8 @@ package main import ( "fmt" + . "github.com/antonmedv/fx/pkg/dict" + . "github.com/antonmedv/fx/pkg/json" "regexp" ) @@ -54,7 +56,7 @@ func (m *model) doSearch(s string) { m.searchInput.Blur() return } - indexes := re.FindAllStringIndex(stringify(m.json), -1) + indexes := re.FindAllStringIndex(Stringify(m.json), -1) m.remapSearchResult(m.json, "", 0, indexes, 0, nil) m.indexSearchResults() m.searchInput.Blur() @@ -79,8 +81,8 @@ func (m *model) remapSearchResult(object interface{}, path string, pos int, inde id, current = m.findRanges(valueRange, s, path, pos, indexes, id, current) return pos + len(s), id, current - case number: - s := object.(number).String() + case Number: + s := object.(Number).String() id, current = m.findRanges(valueRange, s, path, pos, indexes, id, current) return pos + len(s), id, current @@ -89,10 +91,10 @@ func (m *model) remapSearchResult(object interface{}, path string, pos int, inde id, current = m.findRanges(valueRange, s, path, pos, indexes, id, current) return pos + len(s), id, current - case *dict: + case *Dict: id, current = m.findRanges(openBracketRange, "{", path, pos, indexes, id, current) pos++ // { - for i, k := range object.(*dict).keys { + for i, k := range object.(*Dict).Keys { subpath := path + "." + k key := fmt.Sprintf("%q", k) @@ -103,8 +105,8 @@ func (m *model) remapSearchResult(object interface{}, path string, pos int, inde id, current = m.findRanges(delimRange, delim, subpath, pos, indexes, id, current) pos += len(delim) - pos, id, current = m.remapSearchResult(object.(*dict).values[k], subpath, pos, indexes, id, current) - if i < len(object.(*dict).keys)-1 { + pos, id, current = m.remapSearchResult(object.(*Dict).Values[k], subpath, pos, indexes, id, current) + if i < len(object.(*Dict).Keys)-1 { comma := "," id, current = m.findRanges(commaRange, comma, subpath, pos, indexes, id, current) pos += len(comma) @@ -114,13 +116,13 @@ func (m *model) remapSearchResult(object interface{}, path string, pos int, inde pos++ // } return pos, id, current - case array: + case Array: id, current = m.findRanges(openBracketRange, "[", path, pos, indexes, id, current) pos++ // [ - for i, v := range object.(array) { + for i, v := range object.(Array) { subpath := fmt.Sprintf("%v[%v]", path, i) pos, id, current = m.remapSearchResult(v, subpath, pos, indexes, id, current) - if i < len(object.(array))-1 { + if i < len(object.(Array))-1 { comma := "," id, current = m.findRanges(commaRange, comma, subpath, pos, indexes, id, current) pos += len(comma) diff --git a/search_test.go b/search_test.go index b1f8e66..f7e76b3 100644 --- a/search_test.go +++ b/search_test.go @@ -1,6 +1,8 @@ package main import ( + . "github.com/antonmedv/fx/pkg/dict" + . "github.com/antonmedv/fx/pkg/json" "github.com/stretchr/testify/require" "regexp" "testing" @@ -15,7 +17,7 @@ func Test_search_values(t *testing.T) { {name: "null", object: nil}, {name: "true", object: true}, {name: "false", object: false}, - {name: "number", object: number("42")}, + {name: "Number", object: Number("42")}, {name: "string", object: "Hello, World!"}, } for _, tt := range tests { @@ -24,7 +26,7 @@ func Test_search_values(t *testing.T) { json: tt.object, } re, _ := regexp.Compile(".+") - str := stringify(m.json) + str := Stringify(m.json) indexes := re.FindAllStringIndex(str, -1) m.remapSearchResult(m.json, "", 0, indexes, 0, nil) @@ -47,10 +49,10 @@ func Test_search_array(t *testing.T) { ^^^^^ ^^^^^^ ` m := &model{ - json: array{"first", "second"}, + json: Array{"first", "second"}, } re, _ := regexp.Compile("\\w+") - indexes := re.FindAllStringIndex(stringify(m.json), -1) + indexes := re.FindAllStringIndex(Stringify(m.json), -1) m.remapSearchResult(m.json, "", 0, indexes, 0, nil) s1 := &searchResult{path: "[0]"} @@ -82,10 +84,10 @@ func Test_search_between_array(t *testing.T) { ^^^^^^^^^^^^^^ ` m := &model{ - json: array{"first", "second"}, + json: Array{"first", "second"}, } re, _ := regexp.Compile("\\w.+\\w") - indexes := re.FindAllStringIndex(stringify(m.json), -1) + indexes := re.FindAllStringIndex(Stringify(m.json), -1) m.remapSearchResult(m.json, "", 0, indexes, 0, nil) s := &searchResult{path: "[0]"} @@ -120,13 +122,13 @@ func Test_search_dict(t *testing.T) { {"key": "hello world"} ^^^^^ ^^^^^^^^^^^^^ ` - d := newDict() - d.set("key", "hello world") + d := NewDict() + d.Set("key", "hello world") m := &model{ json: d, } re, _ := regexp.Compile("\"[\\w\\s]+\"") - indexes := re.FindAllStringIndex(stringify(m.json), -1) + indexes := re.FindAllStringIndex(Stringify(m.json), -1) m.remapSearchResult(m.json, "", 0, indexes, 0, nil) s1 := &searchResult{path: ".key"} @@ -157,14 +159,14 @@ func Test_search_dict_with_array(t *testing.T) { {"first": [1,2],"second": []} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ` - d := newDict() - d.set("first", array{number("1"), number("2")}) - d.set("second", array{}) + d := NewDict() + d.Set("first", Array{Number("1"), Number("2")}) + d.Set("second", Array{}) m := &model{ json: d, } re, _ := regexp.Compile(".+") - indexes := re.FindAllStringIndex(stringify(m.json), -1) + indexes := re.FindAllStringIndex(Stringify(m.json), -1) m.remapSearchResult(m.json, "", 0, indexes, 0, nil) s := &searchResult{path: ""} diff --git a/theme.go b/theme.go deleted file mode 100644 index c9c3045..0000000 --- a/theme.go +++ /dev/null @@ -1,185 +0,0 @@ -package main - -import ( - "github.com/charmbracelet/lipgloss" - "github.com/mazznoer/colorgrad" - "strings" -) - -type Theme struct { - cursor Color - syntax Color - preview Color - statusBar Color - search Color - key func(i, len int) Color - string Color - null Color - boolean Color - number Color -} -type Color func(s string) string - -func fg(color string) Color { - return lipgloss.NewStyle().Foreground(lipgloss.Color(color)).Render -} - -func boldFg(color string) Color { - return lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color(color)).Render -} - -var ( - defaultCursor = lipgloss.NewStyle().Reverse(true).Render - defaultPreview = lipgloss.NewStyle().Bold(true).Foreground(lipgloss.Color("8")).Render - defaultStatusBar = lipgloss.NewStyle().Background(lipgloss.Color("7")).Foreground(lipgloss.Color("0")).Render - defaultSearch = lipgloss.NewStyle().Background(lipgloss.Color("11")).Foreground(lipgloss.Color("16")).Render - defaultNull = fg("8") -) - -var themes = map[string]Theme{ - "0": { - cursor: defaultCursor, - syntax: noColor, - preview: noColor, - statusBar: noColor, - search: defaultSearch, - key: func(_, _ int) Color { return noColor }, - string: noColor, - null: noColor, - boolean: noColor, - number: noColor, - }, - "1": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return boldFg("4") }, - string: boldFg("2"), - null: defaultNull, - boolean: boldFg("3"), - number: boldFg("6"), - }, - "2": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return fg("#00F5D4") }, - string: fg("#00BBF9"), - null: defaultNull, - boolean: fg("#F15BB5"), - number: fg("#9B5DE5"), - }, - "3": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return fg("#faf0ca") }, - string: fg("#f4d35e"), - null: defaultNull, - boolean: fg("#ee964b"), - number: fg("#ee964b"), - }, - "4": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return fg("#4D96FF") }, - string: fg("#6BCB77"), - null: defaultNull, - boolean: fg("#FF6B6B"), - number: fg("#FFD93D"), - }, - "5": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return boldFg("42") }, - string: boldFg("213"), - null: defaultNull, - boolean: boldFg("201"), - number: boldFg("201"), - }, - "6": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return gradient("rgb(125,110,221)", "rgb(90%,45%,97%)", "hsl(229,79%,85%)") }, - string: fg("195"), - null: defaultNull, - boolean: fg("195"), - number: fg("195"), - }, - "7": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: func(_, _ int) Color { return gradient("rgb(123,216,96)", "rgb(255,255,255)") }, - string: noColor, - null: defaultNull, - boolean: noColor, - number: noColor, - }, - "8": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: gradientKeys("#ff0000", "#ff8700", "#ffd300", "#deff0a", "#a1ff0a", "#0aff99", "#0aefff", "#147df5", "#580aff", "#be0aff"), - string: noColor, - null: defaultNull, - boolean: noColor, - number: noColor, - }, - "9": { - cursor: defaultCursor, - syntax: noColor, - preview: defaultPreview, - statusBar: defaultStatusBar, - search: defaultSearch, - key: gradientKeys("rgb(34,126,34)", "rgb(168,251,60)"), - string: gradient("rgb(34,126,34)", "rgb(168,251,60)"), - null: defaultNull, - boolean: noColor, - number: noColor, - }, -} - -func noColor(s string) string { - return s -} - -func gradient(colors ...string) Color { - grad, _ := colorgrad.NewGradient().HtmlColors(colors...).Build() - return func(s string) string { - runes := []rune(s) - colors := grad.ColorfulColors(uint(len(runes))) - var out strings.Builder - for i, r := range runes { - style := lipgloss.NewStyle().Foreground(lipgloss.Color(colors[i].Hex())) - out.WriteString(style.Render(string(r))) - } - return out.String() - } -} - -func gradientKeys(colors ...string) func(i, len int) Color { - grad, _ := colorgrad.NewGradient().HtmlColors(colors...).Build() - return func(i, len int) Color { - return lipgloss.NewStyle().Foreground(lipgloss.Color(grad.At(float64(i) / float64(len)).Hex())).Render - } -} diff --git a/traverse.go b/traverse.go deleted file mode 100644 index 65ca634..0000000 --- a/traverse.go +++ /dev/null @@ -1,38 +0,0 @@ -package main - -type iterator struct { - object interface{} - path, parent string -} - -func dfs(object interface{}, f func(it iterator)) { - sub(iterator{object: object}, f) -} - -func sub(it iterator, f func(it iterator)) { - f(it) - switch it.object.(type) { - case *dict: - keys := it.object.(*dict).keys - for _, k := range keys { - subpath := it.path + "." + k - value, _ := it.object.(*dict).get(k) - sub(iterator{ - object: value, - path: subpath, - parent: it.path, - }, f) - } - - case array: - slice := it.object.(array) - for i, value := range slice { - subpath := accessor(it.path, i) - sub(iterator{ - object: value, - path: subpath, - parent: it.path, - }, f) - } - } -} diff --git a/viewport.go b/viewport.go index fa57bb6..ce8bd92 100644 --- a/viewport.go +++ b/viewport.go @@ -81,8 +81,8 @@ func (m *model) LineDown(n int) { return } - // Make sure the number of lines by which we're going to scroll isn't - // greater than the number of lines we actually have left before we reach + // Make sure the Number of lines by which we're going to scroll isn't + // greater than the Number of lines we actually have left before we reach // the bottom. m.SetOffset(m.offset + n) } @@ -92,8 +92,8 @@ func (m *model) LineUp(n int) { return } - // Make sure the number of lines by which we're going to scroll isn't - // greater than the number of lines we are from the top. + // Make sure the Number of lines by which we're going to scroll isn't + // greater than the Number of lines we are from the top. m.SetOffset(m.offset - n) }