[
  {
    "path": ".circleci/config.yml",
    "content": "version: 2.1\njobs:\n  test:\n    docker:\n      - image: circleci/golang:latest\n    steps:\n      - checkout\n      - run: go test -v ./\n      - run: go test -v ./cmd/jid\n  release:\n    docker:\n      - image: circleci/golang:latest\n    steps:\n      - checkout\n      - run: \n          name: build jid using goreleaser\n          command: curl -sL https://git.io/goreleaser | bash\nworkflows:\n  version: 2.1\n  test_and_release:\n    jobs:\n      - test:\n          filters:\n            tags:\n              only: /v[0-9]+(\\.[0-9]+)*(-.*)*/\n      - release:\n          requires:\n            - test\n          filters:\n            branches:\n              ignore: /.*/\n            tags:\n              only: /v[0-9]+(\\.[0-9]+)*(-.*)*/\n"
  },
  {
    "path": ".gitignore",
    "content": "# Compiled Object files, Static and Dynamic libs (Shared Objects)\n*.o\n*.a\n*.so\n\n# Folders\n_obj\n_test\n\n# Architecture specific extensions/prefixes\n*.[568vq]\n[568vq].out\n\n*.cgo1.go\n*.cgo2.c\n_cgo_defun.c\n_cgo_gotypes.go\n_cgo_export.*\n\n_testmain.go\n\n*.exe\n*.test\n*.prof\n*.swp\n*.test\ntest.*\n\n# jid package\njid\n*.out\n*.log\ndist\n\n.idea\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "# This is an example goreleaser.yaml file with some sane defaults.\n# Make sure to check the documentation at http://goreleaser.com\nbefore:\n  hooks:\n    # you may remove this if you don't use vgo\n    - go mod download\n    # you may remove this if you don't need go generate\n    - go generate ./...\nbuilds:\n- env:\n  - CGO_ENABLED=0\n  main: ./cmd/jid/jid.go\n  ldflags:\n  - -s -w\n  goos:\n    - windows\n    - openbsd\n    - netbsd\n    - linux\n    - freebsd\n    - darwin\n  goarch:\n    - arm64\n    - amd64\n    - 386\narchive:\n  name_template: \"{{ .ProjectName }}_{{ .Os }}_{{ .Arch }}\"\n  format: zip\n  files:\n    - none*\nchecksum:\n  name_template: 'checksums.txt'\nsnapshot:\n  name_template: \"{{ .Tag }}-next\"\nchangelog:\n  sort: asc\n  filters:\n    exclude:\n    - '^docs:'\n    - '^test:'\n# for Validation\n#release:\n#  disable: true\n"
  },
  {
    "path": "ChangeLog",
    "content": "ChangeLog\n=======\n\n### 0.7.2 - Jan 23 2017\n\nBugFix:\n* fix version number\n\n### 0.7.1 - Jan 5 2017\n\nFeatures:\n* scroll to bottom and top\n\n### 0.7.0 - Jan 4 2017\n\nFeatures:\n* output with color\n* monochrome mode option\n\nBugFix:\n* multibyte Query & Json key/value\n\nModified README\n\n### 0.6.3 - 15 Dec 2016\n\nFeatures:\n* Add `-help` and `-h` command for show a help\n* Add Ctrl-U command and a query behavior\n\nChange Behaviors:\n* Force insert `.`\n\n### 0.6.2 - 9 Dec 2016\n\nFeatures:\n* Add --version flag for homebrew test\n\n### 0.6.1 - 5 Dec 2016\n\nFeatures:\n* Get first argument of `jid` for initial query\n\nChange Behaviors:\n* Auto input . at first character of query\n"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2016 simeji\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# jid\n\n[![Circle CI](https://circleci.com/gh/simeji/jid/tree/master.svg?style=shield)](https://circleci.com/gh/simeji/jid/tree/master)\n\nJson Incremental Digger\n\nIt's a very simple tool.  \nYou can drill down JSON interactively by using filtering queries like [jq](https://stedolan.github.io/jq/).\n\n**Suggestion** and **Auto completion** of this tool will provide you a very comfortable JSON drill down.\n\n## Demo\n\n![demo-jid-main](https://github.com/simeji/jid/wiki/images/demo-jid-main-640-colorize.gif)\n\n## Installation\n\n* [With HomeBrew (for macOS)](#with-homebrew-for-macos)  \n* [With MacPorts (for macOS)](#with-macports-for-macos)  \n* [With pkg (for FreeBSD)](#with-pkg-for-freebsd)\n* [With scoop (for Windows)](#with-scoop-for-windows)\n* [Other package management system](#other-package-management-systems)\n* [Simply use \"jid\" command](#simply-use-jid-command)  \n* [Build](#build)  \n\n### With HomeBrew (for macOS)\n\n```\nbrew install jid\n```\n\n### With MacPorts (for macOS)\n\n```\nsudo port install jid\n```\n\n### With pkg (for FreeBSD)\n\n```\npkg install jid\n```\n\n### With scoop (for Windows)\n\n```\nscoop install jid\n```\n\n### Other package management systems\n\nJid can install by package management systems of below OS.\n\n[![Packaging status](https://repology.org/badge/vertical-allrepos/jid.svg)](https://repology.org/metapackage/jid/versions)\n\n\n### Simply use \"jid\" command\n\nIf you simply want to use `jid` command, please download binary from below.\n\nhttps://github.com/simeji/jid/releases\n\n## Build\n\n```\ngo install github.com/simeji/jid/cmd/jid@latest\n```\n\n## Usage\n\n### Quick start\n\n* [simple json example](#simple-json-example)  \n* [simple json example2](#simple-json-example2)  \n* [with initial query](#with-initial-query)  \n* [with curl](#with-curl)  \n\n#### simple json example\n\nPlease execute the below command.\n\n```\necho '{\"aa\":\"2AA2\",\"bb\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":321}}'| jid\n```\n\nthen, jid will be running.\n\nYou can dig JSON data incrementally.\n\nWhen you enter `.bb.aaa[2]`, you will see the following.\n\n```\n[Filter]> .bb.aaa[2]\n[\n  1,\n  2\n]\n```\n\nThen, you press Enter key and output `[1,2]` and exit.\n\n#### simple json example2\n\nThis json is used by [demo section](https://github.com/simeji/jid#demo).\n```\necho '{\"info\":{\"date\":\"2016-10-23\",\"version\":1.0},\"users\":[{\"name\":\"simeji\",\"uri\":\"https://github.com/simeji\",\"id\":1},{\"name\":\"simeji2\",\"uri\":\"https://example.com/simeji\",\"id\":2},{\"name\":\"simeji3\",\"uri\":\"https://example.com/simeji3\",\"id\":3}],\"userCount\":3}}'|jid\n```\n\n#### With a initial query\n\nFirst argument of `jid` is initial query.\n(Use JSON same as [Demo](#demo))\n\n![demo-jid-with-query](https://github.com/simeji/jid/wiki/images/demo-jid-with-query-640.gif)\n\n#### with curl\n\nSample for using [RDAP](https://datatracker.ietf.org/wg/weirds/documents/) data.\n\n```\ncurl -s http://rdg.afilias.info/rdap/domain/example.info | jid\n```\n\n#### Load JSON from a file\n\n```\njid < file.json\n```\n\n## Keymaps\n\n|key|description|\n|:-----------|:----------|\n|`TAB` / `CTRL` + `I` |Show available items and choice them|\n|`CTRL` + `W` |Delete from the cursor to the start of the word|\n|`CTRL` + `U` |Delete whole query|\n|`CTRL` + `F` / Right Arrow (:arrow_right:)|Move cursor a character to the right|\n|`CTRL` + `B` / Left Arrow (:arrow_left:)|Move cursor a character to the left|\n|`CTRL` + `A`|To the first character of the 'Filter'|\n|`CTRL` + `E`|To the end of the 'Filter'|\n|`CTRL` + `J`|Scroll json buffer 1 line downwards|\n|`CTRL` + `K`|Scroll json buffer 1 line upwards|\n|`CTRL` + `G`|Scroll json buffer to bottom|\n|`CTRL` + `T`|Scroll json buffer to top|\n|`CTRL` + `N`|Scroll json buffer 'Page Down'|\n|`CTRL` + `P`|Scroll json buffer 'Page Up'|\n|`CTRL` + `L`|Change view mode whole json or keys (only object)|\n|`ESC`|Hide a candidate box|\n\n### Option\n\n|option|description|\n|:-----------|:----------|\n|First argument ($1) | Initial query|\n|-h | print a help|\n|-help | print a help|\n|-version | print the version and exit|\n|-q | Output query mode (for jq)|\n|-M | monochrome output mode|\n"
  },
  {
    "path": "engine.go",
    "content": "package jid\n\nimport (\n\t\"io\"\n\t\"strings\"\n\n\ttermbox \"github.com/nsf/termbox-go\"\n)\n\nconst (\n\tDefaultY     int    = 1\n\tFilterPrompt string = \"[Filter]> \"\n)\n\ntype EngineInterface interface {\n\tRun() EngineResultInterface\n\tGetQuery() QueryInterface\n}\n\ntype EngineResultInterface interface {\n\tGetQueryString() string\n\tGetContent() string\n\tGetError() error\n}\n\ntype Engine struct {\n\tmanager        *JsonManager\n\tquery          QueryInterface\n\tqueryCursorIdx int\n\tterm           *Terminal\n\tcomplete       []string\n\tkeymode        bool\n\tcandidates     []string\n\tcandidatemode  bool\n\tcandidateidx   int\n\tcontentOffset  int\n\tqueryConfirm   bool\n\tprettyResult   bool\n}\n\ntype EngineAttribute struct {\n\tDefaultQuery string\n\tMonochrome   bool\n\tPrettyResult bool\n}\n\nfunc NewEngine(s io.Reader, ea *EngineAttribute) (EngineInterface, error) {\n\tj, err := NewJsonManager(s)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\te := &Engine{\n\t\tmanager:       j,\n\t\tterm:          NewTerminal(FilterPrompt, DefaultY, ea.Monochrome),\n\t\tquery:         NewQuery([]rune(ea.DefaultQuery)),\n\t\tcomplete:      []string{\"\", \"\"},\n\t\tkeymode:       false,\n\t\tcandidates:    []string{},\n\t\tcandidatemode: false,\n\t\tcandidateidx:  0,\n\t\tcontentOffset: 0,\n\t\tqueryConfirm:  false,\n\t\tprettyResult:  ea.PrettyResult,\n\t}\n\te.queryCursorIdx = e.query.Length()\n\treturn e, nil\n}\n\ntype EngineResult struct {\n\tcontent string\n\tqs      string\n\terr     error\n}\n\nfunc (er *EngineResult) GetQueryString() string {\n\treturn er.qs\n}\n\nfunc (er *EngineResult) GetContent() string {\n\treturn er.content\n}\nfunc (er *EngineResult) GetError() error {\n\treturn er.err\n}\n\nfunc (e *Engine) GetQuery() QueryInterface {\n\treturn e.query\n}\n\nfunc (e *Engine) Run() EngineResultInterface {\n\n\terr := termbox.Init()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer termbox.Close()\n\n\tvar contents []string\n\n\tfor {\n\n\t\tif e.query.StringGet() == \"\" {\n\t\t\te.query.StringSet(\".\")\n\t\t\te.queryCursorIdx = e.query.Length()\n\t\t}\n\n\t\tbl := len(contents)\n\t\tcontents = e.getContents()\n\t\te.setCandidateData()\n\t\te.queryConfirm = false\n\t\tif bl != len(contents) {\n\t\t\te.contentOffset = 0\n\t\t}\n\n\t\tta := &TerminalDrawAttributes{\n\t\t\tQuery:           e.query.StringGet(),\n\t\t\tContents:        contents,\n\t\t\tCandidateIndex:  e.candidateidx,\n\t\t\tContentsOffsetY: e.contentOffset,\n\t\t\tComplete:        e.complete[0],\n\t\t\tCandidates:      e.candidates,\n\t\t\tCursorOffset:    e.query.IndexOffset(e.queryCursorIdx),\n\t\t}\n\t\terr = e.term.Draw(ta)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tswitch ev := termbox.PollEvent(); ev.Type {\n\t\tcase termbox.EventKey:\n\t\t\tswitch ev.Key {\n\t\t\tcase 0:\n\t\t\t\te.inputChar(ev.Ch)\n\t\t\tcase termbox.KeyBackspace, termbox.KeyBackspace2:\n\t\t\t\te.deleteChar()\n\t\t\tcase termbox.KeyTab:\n\t\t\t\te.tabAction()\n\t\t\tcase termbox.KeyArrowLeft, termbox.KeyCtrlB:\n\t\t\t\te.moveCursorBackward()\n\t\t\tcase termbox.KeyArrowRight, termbox.KeyCtrlF:\n\t\t\t\te.moveCursorForward()\n\t\t\tcase termbox.KeyHome, termbox.KeyCtrlA:\n\t\t\t\te.moveCursorToTop()\n\t\t\tcase termbox.KeyEnd, termbox.KeyCtrlE:\n\t\t\t\te.moveCursorToEnd()\n\t\t\tcase termbox.KeyCtrlK:\n\t\t\t\te.scrollToAbove()\n\t\t\tcase termbox.KeyCtrlJ:\n\t\t\t\te.scrollToBelow()\n\t\t\tcase termbox.KeyCtrlG:\n\t\t\t\te.scrollToBottom(len(contents))\n\t\t\tcase termbox.KeyCtrlT:\n\t\t\t\te.scrollToTop()\n\t\t\tcase termbox.KeyCtrlN:\n\t\t\t\t_, h := termbox.Size()\n\t\t\t\te.scrollPageDown(len(contents), h)\n\t\t\tcase termbox.KeyCtrlP:\n\t\t\t\t_, h := termbox.Size()\n\t\t\t\te.scrollPageUp(h)\n\t\t\tcase termbox.KeyCtrlL:\n\t\t\t\te.toggleKeymode()\n\t\t\tcase termbox.KeyCtrlU:\n\t\t\t\te.deleteLineQuery()\n\t\t\tcase termbox.KeyCtrlW:\n\t\t\t\te.deleteWordBackward()\n\t\t\tcase termbox.KeyEsc:\n\t\t\t\te.escapeCandidateMode()\n\t\t\tcase termbox.KeyEnter:\n\t\t\t\tif !e.candidatemode {\n\t\t\t\t\tvar cc string\n\t\t\t\t\tvar err error\n\t\t\t\t\tif e.prettyResult {\n\t\t\t\t\t\tcc, _, _, err = e.manager.GetPretty(e.query, true)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcc, _, _, err = e.manager.Get(e.query, true)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn &EngineResult{\n\t\t\t\t\t\tcontent: cc,\n\t\t\t\t\t\tqs:      e.query.StringGet(),\n\t\t\t\t\t\terr:     err,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\te.confirmCandidate()\n\t\t\tcase termbox.KeyCtrlC:\n\t\t\t\treturn &EngineResult{}\n\t\t\tdefault:\n\t\t\t}\n\t\tcase termbox.EventError:\n\t\t\tpanic(ev.Err)\n\t\t\tbreak\n\t\tdefault:\n\t\t}\n\t}\n}\n\nfunc (e *Engine) getContents() []string {\n\tvar c string\n\tvar contents []string\n\tc, e.complete, e.candidates, _ = e.manager.GetPretty(e.query, e.queryConfirm)\n\tif e.keymode {\n\t\tcontents = e.candidates\n\t} else {\n\t\tcontents = strings.Split(c, \"\\n\")\n\t}\n\treturn contents\n}\n\nfunc (e *Engine) setCandidateData() {\n\tif l := len(e.candidates); e.complete[0] == \"\" && l > 1 {\n\t\tif e.candidateidx >= l {\n\t\t\te.candidateidx = 0\n\t\t}\n\t} else {\n\t\te.candidatemode = false\n\t}\n\tif !e.candidatemode {\n\t\te.candidateidx = 0\n\t\te.candidates = []string{}\n\t}\n}\n\nfunc (e *Engine) confirmCandidate() {\n\t_, _ = e.query.PopKeyword()\n\t_ = e.query.StringAdd(\".\")\n\t_ = e.query.StringAdd(e.candidates[e.candidateidx])\n\te.queryCursorIdx = e.query.Length()\n\te.queryConfirm = true\n}\n\nfunc (e *Engine) deleteChar() {\n\tif i := e.queryCursorIdx - 1; i > 0 {\n\t\t_ = e.query.Delete(i)\n\t\te.queryCursorIdx--\n\t}\n\n}\n\nfunc (e *Engine) deleteLineQuery() {\n\t_ = e.query.StringSet(\"\")\n\te.queryCursorIdx = 0\n}\n\nfunc (e *Engine) scrollToBelow() {\n\te.contentOffset++\n}\n\nfunc (e *Engine) scrollToAbove() {\n\tif o := e.contentOffset - 1; o >= 0 {\n\t\te.contentOffset = o\n\t}\n}\n\nfunc (e *Engine) scrollToBottom(rownum int) {\n\te.contentOffset = rownum - 1\n}\n\nfunc (e *Engine) scrollToTop() {\n\te.contentOffset = 0\n}\n\nfunc (e *Engine) scrollPageDown(rownum int, height int) {\n\tco := rownum - 1\n\tif o := rownum - e.contentOffset; o > height {\n\t\tco = e.contentOffset + (height - DefaultY)\n\t}\n\te.contentOffset = co\n}\n\nfunc (e *Engine) scrollPageUp(height int) {\n\tco := 0\n\tif o := e.contentOffset - (height - DefaultY); o > 0 {\n\t\tco = o\n\t}\n\te.contentOffset = co\n}\n\nfunc (e *Engine) toggleKeymode() {\n\te.keymode = !e.keymode\n}\nfunc (e *Engine) deleteWordBackward() {\n\tif k, _ := e.query.StringPopKeyword(); k != \"\" && !strings.Contains(k, \"[\") {\n\t\t_ = e.query.StringAdd(\".\")\n\t}\n\te.queryCursorIdx = e.query.Length()\n}\nfunc (e *Engine) tabAction() {\n\tif !e.candidatemode {\n\t\te.candidatemode = true\n\t\tif e.complete[0] != e.complete[1] && e.complete[0] != \"\" {\n\t\t\tif k, _ := e.query.StringPopKeyword(); !strings.Contains(k, \"[\") {\n\t\t\t\t_ = e.query.StringAdd(\".\")\n\t\t\t}\n\t\t\t_ = e.query.StringAdd(e.complete[1])\n\t\t} else {\n\t\t\t_ = e.query.StringAdd(e.complete[0])\n\t\t}\n\t} else {\n\t\te.candidateidx = e.candidateidx + 1\n\t}\n\te.queryCursorIdx = e.query.Length()\n}\nfunc (e *Engine) escapeCandidateMode() {\n\te.candidatemode = false\n}\nfunc (e *Engine) inputChar(ch rune) {\n\t_ = e.query.Insert([]rune{ch}, e.queryCursorIdx)\n\te.queryCursorIdx++\n}\n\nfunc (e *Engine) moveCursorBackward() {\n\tif i := e.queryCursorIdx - 1; i >= 0 {\n\t\te.queryCursorIdx--\n\t}\n}\n\nfunc (e *Engine) moveCursorForward() {\n\tif e.query.Length() > e.queryCursorIdx {\n\t\te.queryCursorIdx++\n\t}\n}\n\nfunc (e *Engine) moveCursorWordBackwark() {\n}\nfunc (e *Engine) moveCursorWordForward() {\n}\nfunc (e *Engine) moveCursorToTop() {\n\te.queryCursorIdx = 0\n}\nfunc (e *Engine) moveCursorToEnd() {\n\te.queryCursorIdx = e.query.Length()\n}\n"
  },
  {
    "path": "engine_test.go",
    "content": "package jid\n\nimport (\n\t\"bytes\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewEngine(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tf, _ := os.Create(\"/dev/null\")\n\te, err := NewEngine(f, &EngineAttribute{\n\t\tDefaultQuery: \"\",\n\t\tMonochrome:   false,\n\t})\n\tassert.Nil(e)\n\tassert.NotNil(err)\n\n\tee := getEngine(`{\"name\":\"go\"}`, \"\")\n\tassert.NotNil(ee)\n\tassert.Equal(\"\", ee.query.StringGet())\n\tassert.Equal(0, ee.queryCursorIdx)\n}\n\nfunc TestNewEngineWithQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\"}`, \".nam\")\n\tassert.Equal(\".nam\", e.query.StringGet())\n\tassert.Equal(4, e.queryCursorIdx)\n\n\te = getEngine(`{\"name\":\"go\"}`, \"nam\")\n\tassert.Equal(\"\", e.query.StringGet())\n\tassert.Equal(0, e.queryCursorIdx)\n\n\te = getEngine(`{\"name\":\"go\"}`, \".nam..\")\n\tassert.Equal(\"\", e.query.StringGet())\n\tassert.Equal(0, e.queryCursorIdx)\n}\n\nfunc TestDeleteChar(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\"}`, \"\")\n\te.query.StringSet(\".name\")\n\te.queryCursorIdx = e.query.Length()\n\n\te.deleteChar()\n\tassert.Equal(\".nam\", e.query.StringGet())\n\tassert.Equal(4, e.queryCursorIdx)\n}\n\nfunc TestDeleteWordBackward(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\"}`, \"\")\n\te.query.StringSet(\".name\")\n\n\te.deleteWordBackward()\n\tassert.Equal(\".\", e.query.StringGet())\n\tassert.Equal(1, e.queryCursorIdx)\n\n\te.query.StringSet(\".name[1]\")\n\te.deleteWordBackward()\n\tassert.Equal(\".name\", e.query.StringGet())\n\tassert.Equal(5, e.queryCursorIdx)\n\n\te.query.StringSet(\".name[\")\n\te.deleteWordBackward()\n\tassert.Equal(\".name\", e.query.StringGet())\n\tassert.Equal(5, e.queryCursorIdx)\n}\n\nfunc TestDeleteLineQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\"}`, \"\")\n\n\te.query.StringSet(\".name\")\n\te.deleteLineQuery()\n\tassert.Equal(\"\", e.query.StringGet())\n\tassert.Equal(0, e.queryCursorIdx)\n}\n\nfunc TestScrollToAbove(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"named\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\tassert.Equal(0, e.contentOffset)\n\te.scrollToAbove()\n\tassert.Equal(0, e.contentOffset)\n\te.contentOffset = 5\n\te.scrollToAbove()\n\tassert.Equal(4, e.contentOffset)\n\te.scrollToAbove()\n\tassert.Equal(3, e.contentOffset)\n}\n\nfunc TestScrollToBelow(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"named\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\te.scrollToBelow()\n\tassert.Equal(1, e.contentOffset)\n\te.scrollToBelow()\n\te.scrollToBelow()\n\tassert.Equal(3, e.contentOffset)\n}\nfunc TestScrollToBottomAndTop(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"named\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\n\te.scrollToBottom(5)\n\tassert.Equal(4, e.contentOffset)\n\n\te.scrollToTop()\n\tassert.Equal(0, e.contentOffset)\n}\n\nfunc TestScrollPageUpDown(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"named\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\n\tcl := len(e.getContents())\n\t// Then DefaultY = 1\n\te.scrollPageDown(cl, 3)\n\tassert.Equal(2, e.contentOffset)\n\te.scrollPageDown(cl, 3)\n\tassert.Equal(4, e.contentOffset)\n\n\te.scrollPageUp(3)\n\tassert.Equal(2, e.contentOffset)\n\n\t// term height changed\n\te.scrollPageDown(cl, 5)\n\tassert.Equal(6, e.contentOffset)\n\n\te.scrollPageDown(cl, 5)\n\tassert.Equal(7, e.contentOffset)\n\n\te.scrollPageDown(cl, 5)\n\tassert.Equal(7, e.contentOffset)\n\n\te.scrollPageUp(5)\n\tassert.Equal(3, e.contentOffset)\n\n\te.scrollPageUp(5)\n\tassert.Equal(0, e.contentOffset)\n\n\te.scrollPageUp(5)\n\tassert.Equal(0, e.contentOffset)\n\n\t// term height > content size + default Y (a filter query line)\n\te.scrollPageDown(cl, 10)\n\tassert.Equal(7, e.contentOffset)\n}\nfunc TestGetContents(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\te := getEngine(`{\"name\":\"go\"}`, \"\")\n\tc := e.getContents()\n\tassert.Equal([]string{`{`, `  \"name\": \"go\"`, \"}\"}, c)\n\tassert.Equal([]string{}, e.candidates)\n\tassert.Equal([]string{\"\", \"\"}, e.complete)\n\n\te = getEngine(`{\"name\":\"go\", \"naming\":\"simeji\", \"foo\":\"bar\"}`, \"\")\n\te.query.StringSet(\".n\")\n\tc = e.getContents()\n\tassert.Equal([]string{`{`, `  \"foo\": \"bar\",`, `  \"name\": \"go\",`, `  \"naming\": \"simeji\"`, \"}\"}, c)\n\tassert.Equal([]string{\"name\", \"naming\"}, e.candidates)\n\tassert.Equal([]string{\"am\", \"nam\"}, e.complete)\n\n\te.keymode = true\n\tc = e.getContents()\n\tassert.Equal([]string{\"name\", \"naming\"}, c)\n\tassert.Equal([]string{\"name\", \"naming\"}, e.candidates)\n\tassert.Equal([]string{\"am\", \"nam\"}, e.complete)\n}\n\nfunc TestSetCandidateData(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\"}`, \"\")\n\n\t// case 1\n\te.candidates = []string{\"test\", \"testing\"}\n\te.complete = []string{\"est\", \"test\"}\n\te.candidatemode = true\n\te.candidateidx = 1\n\n\te.setCandidateData()\n\tassert.False(e.candidatemode)\n\tassert.Zero(e.candidateidx)\n\tassert.Equal([]string{}, e.candidates)\n\n\t// case 2\n\te.candidates = []string{\"test\"}\n\te.complete = []string{\"\", \"test\"}\n\te.candidatemode = true\n\te.candidateidx = 1\n\n\te.setCandidateData()\n\tassert.False(e.candidatemode)\n\tassert.Zero(e.candidateidx)\n\tassert.Equal([]string{}, e.candidates)\n\n\t// case 3\n\te.candidates = []string{\"test\", \"testing\"}\n\te.complete = []string{\"\", \"test\"}\n\te.candidatemode = true\n\te.candidateidx = 2\n\n\te.setCandidateData()\n\tassert.True(e.candidatemode)\n\tassert.Zero(e.candidateidx)\n\tassert.Equal([]string{\"test\", \"testing\"}, e.candidates)\n\n\t// case 4\n\te.candidates = []string{\"test\", \"testing\"}\n\te.complete = []string{\"\", \"test\"}\n\te.candidatemode = true\n\te.candidateidx = 1\n\n\te.setCandidateData()\n\tassert.True(e.candidatemode)\n\tassert.Equal(1, e.candidateidx)\n\tassert.Equal([]string{\"test\", \"testing\"}, e.candidates)\n\n\t// case 4\n\te.candidates = []string{\"test\", \"testing\"}\n\te.complete = []string{\"\", \"test\"}\n\te.candidatemode = false\n\te.candidateidx = 1\n\n\te.setCandidateData()\n\tassert.False(e.candidatemode)\n\tassert.Equal(0, e.candidateidx)\n\tassert.Equal([]string{}, e.candidates)\n\n}\n\nfunc TestConfirmCandidate(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\te.query.StringSet(\".\")\n\te.queryConfirm = false\n\te.candidates = []string{\"test\", \"testing\", \"foo\"}\n\n\te.candidateidx = 0\n\te.confirmCandidate()\n\tassert.Equal(\".test\", e.query.StringGet())\n\tassert.True(e.queryConfirm)\n\tassert.Equal(5, e.queryCursorIdx)\n\n\te.candidateidx = 2\n\te.confirmCandidate()\n\tassert.Equal(\".foo\", e.query.StringGet())\n\n\tassert.True(e.queryConfirm)\n\tassert.Equal(4, e.queryCursorIdx)\n\n\te = getEngine(`{\"name\":\"go\"}`, \"\")\n\te.query.StringSet(\".name.hoge\")\n\te.candidates = []string{\"aaa\", \"bbb\", \"ccc\"}\n\te.candidateidx = 1\n\te.confirmCandidate()\n\n\tassert.True(e.queryConfirm)\n\tassert.Equal(9, e.queryCursorIdx)\n\tassert.Equal(\".name.bbb\", e.query.StringGet())\n}\n\nfunc TestCtrllAction(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\tassert.False(e.keymode)\n\te.toggleKeymode()\n\tassert.True(e.keymode)\n\te.toggleKeymode()\n\tassert.False(e.keymode)\n}\n\nfunc TestTabAction(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\te.query.StringSet(\".namet\")\n\te.complete = []string{\"est\", \"NameTest\"}\n\n\te.candidatemode = false\n\te.tabAction()\n\tassert.Equal(\".NameTest\", e.query.StringGet())\n\n\t_, e.complete, _, _ = e.manager.GetPretty(e.query, true)\n\te.candidatemode = false\n\te.tabAction()\n\tassert.Equal(\".NameTest[\", e.query.StringGet())\n\n\t_, e.complete, _, _ = e.manager.GetPretty(e.query, true)\n\te.candidatemode = false\n\te.tabAction()\n\tassert.Equal(\".NameTest[\", e.query.StringGet())\n}\n\nfunc TestEscAction(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\",\"NameTest\":[1,2,3]}`, \"\")\n\tassert.False(e.candidatemode)\n\te.escapeCandidateMode()\n\tassert.False(e.candidatemode)\n\te.candidatemode = true\n\te.escapeCandidateMode()\n\tassert.False(e.candidatemode)\n}\n\nfunc TestInputChar(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"go\"}`, \"\")\n\te.query.StringSet(\".name\")\n\te.queryCursorIdx = e.query.Length()\n\tassert.Equal(5, e.queryCursorIdx)\n\n\te.inputChar('n')\n\tassert.Equal(\".namen\", e.query.StringGet())\n\tassert.Equal(6, e.queryCursorIdx)\n\n\te.inputChar('.')\n\tassert.Equal(\".namen.\", e.query.StringGet())\n\tassert.Equal(7, e.queryCursorIdx)\n}\n\nfunc TestMoveCursorForwardAndBackward(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"simeji\"}`, \"\")\n\te.query.StringSet(\".ne\")\n\n\te.moveCursorForward()\n\tassert.Equal(1, e.queryCursorIdx)\n\te.moveCursorForward()\n\tassert.Equal(2, e.queryCursorIdx)\n\te.moveCursorForward()\n\tassert.Equal(3, e.queryCursorIdx)\n\te.moveCursorForward()\n\tassert.Equal(3, e.queryCursorIdx)\n\n\te.moveCursorBackward()\n\tassert.Equal(2, e.queryCursorIdx)\n\te.moveCursorBackward()\n\tassert.Equal(1, e.queryCursorIdx)\n\te.moveCursorBackward()\n\tassert.Equal(0, e.queryCursorIdx)\n\te.moveCursorBackward()\n\tassert.Equal(0, e.queryCursorIdx)\n}\n\nfunc TestMoveCursorToTopAndEnd(t *testing.T) {\n\tvar assert = assert.New(t)\n\te := getEngine(`{\"name\":\"simeji\"}`, \"\")\n\te.query.StringSet(\".ne\")\n\n\te.moveCursorToTop()\n\tassert.Zero(e.queryCursorIdx)\n\n\te.moveCursorToEnd()\n\tassert.Equal(3, e.queryCursorIdx)\n}\n\nfunc getEngine(j string, qs string) *Engine {\n\tr := bytes.NewBufferString(j)\n\te, _ := NewEngine(r, &EngineAttribute{\n\t\tDefaultQuery: qs,\n\t\tMonochrome:   false,\n\t})\n\tee := e.(*Engine)\n\treturn ee\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/simeji/jid\n\ngo 1.20\n\nrequire (\n\tgithub.com/bitly/go-simplejson v0.5.0\n\tgithub.com/mattn/go-runewidth v0.0.4\n\tgithub.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed\n\tgithub.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5\n\tgithub.com/pkg/errors v0.8.0\n\tgithub.com/stretchr/testify v1.8.1\n)\n\nrequire (\n\tgithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/fatih/color v1.7.0 // indirect\n\tgithub.com/kr/pretty v0.3.1 // indirect\n\tgithub.com/mattn/go-colorable v0.0.9 // indirect\n\tgithub.com/mattn/go-isatty v0.0.4 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgolang.org/x/sys v0.10.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y=\ngithub.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=\ngithub.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=\ngithub.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4=\ngithub.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=\ngithub.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=\ngithub.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=\ngithub.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=\ngithub.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=\ngithub.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed h1:bAVGG6B+R5qpSylrrA+BAMrzYkdAoiTaKPVxRB+4cyM=\ngithub.com/nsf/termbox-go v0.0.0-20181027232701-60ab7e3d12ed/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ=\ngithub.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5 h1:d+C3xJdxZT7wNlxqEwbXn3R355CwAhYBL9raVNfSnK0=\ngithub.com/nwidger/jsoncolor v0.0.0-20170215171346-75a6de4340e5/go.mod h1:GYFm0zZgTNeoK1QxuIofRDasy2ibmaJZhZLzwsMXUF4=\ngithub.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=\ngithub.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=\ngithub.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=\ngithub.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=\ngithub.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngolang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=\ngolang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "json_manager.go",
    "content": "package jid\n\nimport (\n\t\"github.com/bitly/go-simplejson\"\n\t\"github.com/pkg/errors\"\n\t\"io\"\n\t\"regexp\"\n\t\"strconv\"\n\t//\"strings\"\n)\n\ntype JsonManager struct {\n\tcurrent    *simplejson.Json\n\torigin     *simplejson.Json\n\tsuggestion *Suggestion\n}\n\nfunc NewJsonManager(reader io.Reader) (*JsonManager, error) {\n\tbuf, err := io.ReadAll(reader)\n\n\tif err != nil {\n\t\treturn nil, errors.Wrap(err, \"invalid data\")\n\t}\n\n\tj, err2 := simplejson.NewJson(buf)\n\n\tif err2 != nil {\n\t\treturn nil, errors.Wrap(err2, \"invalid json format\")\n\t}\n\n\tjson := &JsonManager{\n\t\torigin:     j,\n\t\tcurrent:    j,\n\t\tsuggestion: NewSuggestion(),\n\t}\n\n\treturn json, nil\n}\n\nfunc (jm *JsonManager) Get(q QueryInterface, confirm bool) (string, []string, []string, error) {\n\tjson, suggestion, candidates, _ := jm.GetFilteredData(q, confirm)\n\n\tdata, enc_err := json.Encode()\n\n\tif enc_err != nil {\n\t\treturn \"\", []string{\"\", \"\"}, []string{\"\", \"\"}, errors.Wrap(enc_err, \"failure json encode\")\n\t}\n\n\treturn string(data), suggestion, candidates, nil\n}\n\nfunc (jm *JsonManager) GetPretty(q QueryInterface, confirm bool) (string, []string, []string, error) {\n\tjson, suggestion, candidates, _ := jm.GetFilteredData(q, confirm)\n\ts, err := json.EncodePretty()\n\tif err != nil {\n\t\treturn \"\", []string{\"\", \"\"}, []string{\"\", \"\"}, errors.Wrap(err, \"failure json encode\")\n\t}\n\treturn string(s), suggestion, candidates, nil\n}\n\nfunc (jm *JsonManager) GetFilteredData(q QueryInterface, confirm bool) (*simplejson.Json, []string, []string, error) {\n\tjson := jm.origin\n\n\tlastKeyword := q.StringGetLastKeyword()\n\tkeywords := q.StringGetKeywords()\n\n\tidx := 0\n\tif l := len(keywords); l == 0 {\n\t\treturn json, []string{\"\", \"\"}, []string{}, nil\n\t} else if l > 0 {\n\t\tidx = l - 1\n\t}\n\tfor _, keyword := range keywords[0:idx] {\n\t\tjson, _ = getItem(json, keyword)\n\t}\n\treg := regexp.MustCompile(`\\[[0-9]*$`)\n\n\tsuggest := jm.suggestion.Get(json, lastKeyword)\n\tcandidateKeys := jm.suggestion.GetCandidateKeys(json, lastKeyword)\n\t// hash\n\tif len(reg.FindString(lastKeyword)) < 1 {\n\t\tcandidateNum := len(candidateKeys)\n\t\tif j, exist := getItem(json, lastKeyword); exist && (confirm || candidateNum == 1) {\n\t\t\tjson = j\n\t\t\tcandidateKeys = []string{}\n\t\t\tif _, err := json.Array(); err == nil {\n\t\t\t\tsuggest = jm.suggestion.Get(json, \"\")\n\t\t\t} else {\n\t\t\t\tsuggest = []string{\"\", \"\"}\n\t\t\t}\n\t\t} else if candidateNum < 1 {\n\t\t\tjson = j\n\t\t\tsuggest = jm.suggestion.Get(json, \"\")\n\t\t}\n\t}\n\treturn json, suggest, candidateKeys, nil\n}\n\nfunc (jm *JsonManager) GetCandidateKeys(q QueryInterface) []string {\n\treturn jm.suggestion.GetCandidateKeys(jm.current, q.StringGetLastKeyword())\n}\n\nfunc getItem(json *simplejson.Json, s string) (*simplejson.Json, bool) {\n\tvar result *simplejson.Json\n\tvar exist bool\n\n\tre := regexp.MustCompile(`\\[([0-9]+)\\]`)\n\tmatches := re.FindStringSubmatch(s)\n\n\tif s == \"\" {\n\t\treturn json, false\n\t}\n\n\t// Query include [\n\tif len(matches) > 0 {\n\t\tindex, _ := strconv.Atoi(matches[1])\n\t\tif a, err := json.Array(); err != nil {\n\t\t\texist = false\n\t\t} else if len(a) < index {\n\t\t\texist = false\n\t\t}\n\t\tresult = json.GetIndex(index)\n\t} else {\n\t\tresult, exist = json.CheckGet(s)\n\t\tif result == nil {\n\t\t\tresult = &simplejson.Json{}\n\t\t}\n\t}\n\treturn result, exist\n}\n\nfunc isEmptyJson(j *simplejson.Json) bool {\n\tswitch j.Interface().(type) {\n\tcase nil:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "json_manager_test.go",
    "content": "package jid\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n\n\tsimplejson \"github.com/bitly/go-simplejson\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewJson(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tr := bytes.NewBufferString(\"{\\\"name\\\":\\\"go\\\"}\")\n\tjm, e := NewJsonManager(r)\n\n\trr := bytes.NewBufferString(\"{\\\"name\\\":\\\"go\\\"}\")\n\tbuf, _ := io.ReadAll(rr)\n\tsj, _ := simplejson.NewJson(buf)\n\n\tassert.Equal(jm, &JsonManager{\n\t\tcurrent:    sj,\n\t\torigin:     sj,\n\t\tsuggestion: NewSuggestion(),\n\t})\n\tassert.Nil(e)\n\n\tassert.Equal(\"go\", jm.current.Get(\"name\").MustString())\n}\n\nfunc TestNewJsonWithError(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tr := bytes.NewBufferString(\"{\\\"name\\\":\\\"go\\\"\")\n\tjm, e := NewJsonManager(r)\n\n\tassert.Nil(jm)\n\tassert.Regexp(\"invalid json format\", e.Error())\n}\n\nfunc TestGet(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tr := bytes.NewBufferString(\"{\\\"name\\\":\\\"go\\\"}\")\n\tjm, _ := NewJsonManager(r)\n\tq := NewQueryWithString(\".name\")\n\tresult, suggest, candidateKeys, err := jm.Get(q, false)\n\n\tassert.Nil(err)\n\tassert.Equal(`\"go\"`, result)\n\tassert.Equal([]string{``, ``}, suggest)\n\tassert.Equal([]string{}, candidateKeys)\n\n\t// data\n\tdata := `{\"abcde\":\"2AA2\",\"abcde_fgh\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"}}`\n\tr = bytes.NewBufferString(data)\n\tjm, _ = NewJsonManager(r)\n\n\t// case 2\n\tq = NewQueryWithString(\".abcde\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Nil(err)\n\t//assert.Equal(`\"2AA2\"`, result)\n\tassert.Equal(`{\"abcde\":\"2AA2\",\"abcde_fgh\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"}}`, result)\n\tassert.Equal([]string{``, \"abcde\"}, suggest)\n\n\t// case 3\n\tq = NewQueryWithString(\".abcde_fgh\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Nil(err)\n\tassert.Equal(`{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"}`, result)\n\tassert.Equal([]string{``, ``}, suggest)\n\n\t// case 4\n\tq = NewQueryWithString(\".abcde_fgh.aaa[2]\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Equal(`[1,2]`, result)\n\n\t// case 5\n\tq = NewQueryWithString(\".abcde_fgh.aaa[3]\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Nil(err)\n\tassert.Equal(`null`, result)\n\n\t// case 6\n\tq = NewQueryWithString(\".abcde_fgh.aa\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Nil(err)\n\tassert.Equal(`{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"}`, result)\n\tassert.Equal([]string{`a`, `aaa`}, suggest)\n\n\t// case 7\n\tq = NewQueryWithString(\".abcde_fgh.ac\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Nil(err)\n\tassert.Equal(`null`, result)\n\tassert.Equal([]string{``, ``}, suggest)\n\n\t// data\n\tdata = `{\"abc\":\"2AA2\",\"def\":{\"aaa\":\"bbb\"}}`\n\tr = bytes.NewBufferString(data)\n\tjm, _ = NewJsonManager(r)\n\n\t// case 2\n\tq = NewQueryWithString(\".def\")\n\tresult, suggest, candidateKeys, err = jm.Get(q, false)\n\tassert.Nil(err)\n\tassert.Equal(`{\"aaa\":\"bbb\"}`, result)\n\tassert.Equal([]string{``, ``}, suggest)\n}\n\nfunc TestGetPretty(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tr := bytes.NewBufferString(\"{\\\"name\\\":\\\"go\\\"}\")\n\tjm, _ := NewJsonManager(r)\n\tq := NewQueryWithString(\".name\")\n\tresult, _, _, err := jm.GetPretty(q, true)\n\n\tassert.Nil(err)\n\tassert.Equal(`\"go\"`, result)\n}\n\nfunc TestGetItem(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\trr := bytes.NewBufferString(`{\"name\":\"go\"}`)\n\tbuf, _ := io.ReadAll(rr)\n\tsj, _ := simplejson.NewJson(buf)\n\n\td, _ := getItem(sj, \"\")\n\tresult, _ := d.Encode()\n\tassert.Equal(`{\"name\":\"go\"}`, string(result))\n\n\td, _ = getItem(sj, \"name\")\n\tresult, _ = d.Encode()\n\tassert.Equal(`\"go\"`, string(result))\n\n\t// case 2\n\trr = bytes.NewBufferString(`{\"name\":\"go\",\"age\":20}`)\n\tbuf, _ = io.ReadAll(rr)\n\tsj, _ = simplejson.NewJson(buf)\n\n\td, _ = getItem(sj, \"age\")\n\tresult, _ = d.Encode()\n\tassert.Equal(\"20\", string(result))\n\n\t// case 3\n\trr = bytes.NewBufferString(`{\"data\":{\"name\":\"go\",\"age\":20}}`)\n\tbuf, _ = io.ReadAll(rr)\n\tsj, _ = simplejson.NewJson(buf)\n\n\td, _ = getItem(sj, \"data\")\n\td2, _ := getItem(d, \"name\")\n\td3, _ := getItem(d, \"age\")\n\tresult2, _ := d2.Encode()\n\tresult3, _ := d3.Encode()\n\n\tassert.Equal(`\"go\"`, string(result2))\n\tassert.Equal(`20`, string(result3))\n\n\t// case 4\n\trr = bytes.NewBufferString(`{\"data\":[{\"name\":\"test\",\"age\":30},{\"name\":\"go\",\"age\":20}]}`)\n\tbuf, _ = io.ReadAll(rr)\n\tsj, _ = simplejson.NewJson(buf)\n\n\td, _ = getItem(sj, \"data\")\n\td2, _ = getItem(d, \"[1]\")\n\td3, _ = getItem(d2, \"name\")\n\tresult, _ = d3.Encode()\n\n\tassert.Equal(`\"go\"`, string(result))\n\n\t// case 5\n\trr = bytes.NewBufferString(`[{\"name\":\"go\",\"age\":20}]`)\n\tbuf, _ = io.ReadAll(rr)\n\tsj, _ = simplejson.NewJson(buf)\n\n\td, _ = getItem(sj, \"\")\n\tresult, _ = d.Encode()\n\tassert.Equal(`[{\"age\":20,\"name\":\"go\"}]`, string(result))\n\n\t// case 6\n\td, _ = getItem(sj, \"[0]\")\n\tresult, _ = d.Encode()\n\tassert.Equal(`{\"age\":20,\"name\":\"go\"}`, string(result))\n\n\t// case 7  key contains '.'\n\trr = bytes.NewBufferString(`{\"na.me\":\"go\",\"age\":20}`)\n\tbuf, _ = io.ReadAll(rr)\n\tsj, _ = simplejson.NewJson(buf)\n\n\td, _ = getItem(sj, \"na.me\")\n\tresult, _ = d.Encode()\n\tassert.Equal(`\"go\"`, string(result))\n\n}\n\nfunc TestGetFilteredData(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\t// data\n\tdata := `{\"abcde\":\"2AA2\",\"abcde_fgh\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"},\"cc\":{\"a\":[3,4]}}`\n\tr := bytes.NewBufferString(data)\n\tjm, _ := NewJsonManager(r)\n\n\t// case 1\n\tq := NewQueryWithString(\".abcde\")\n\tresult, s, c, err := jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ := result.Encode()\n\tassert.Equal(`{\"abcde\":\"2AA2\",\"abcde_fgh\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"},\"cc\":{\"a\":[3,4]}}`, string(d))\n\t//assert.Equal(`\"2AA2\"`, string(d))\n\tassert.Equal([]string{``, `abcde`}, s)\n\tassert.Equal([]string{\"abcde\", \"abcde_fgh\"}, c)\n\n\t// case 2\n\tq = NewQueryWithString(\".abcde_fgh\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"}`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\tassert.Equal([]string{}, c)\n\n\t// case 3\n\tq = NewQueryWithString(\".abcde_fgh.aaa[2]\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`[1,2]`, string(d))\n\tassert.Equal([]string{`[`, `[`}, s)\n\n\t// case 4\n\tq = NewQueryWithString(\".abcde_fgh.aaa[3]\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`null`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\n\t// case 5\n\tq = NewQueryWithString(\".abcde_fgh.aaa\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`[123,\"cccc\",[1,2]]`, string(d))\n\tassert.Equal([]string{`[`, `[`}, s)\n\n\t// case 6\n\tq = NewQueryWithString(\".abcde_fgh.aa\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"}`, string(d))\n\tassert.Equal([]string{`a`, `aaa`}, s)\n\n\t// case 7\n\tq = NewQueryWithString(\".abcde_fgh.aaa[\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`[123,\"cccc\",[1,2]]`, string(d))\n\tassert.Equal([]string{``, `[`}, s)\n\n\t// case 8\n\tq = NewQueryWithString(\".\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"abcde\":\"2AA2\",\"abcde_fgh\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"},\"cc\":{\"a\":[3,4]}}`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\n\t// case 9\n\tq = NewQueryWithString(\".cc.\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"a\":[3,4]}`, string(d))\n\tassert.Equal([]string{`a`, `a`}, s)\n\tassert.Equal([]string{\"a\"}, c)\n\n\t// case 2-1\n\tdata = `{\"arraytest\":[{\"aaa\":123,\"aab\":234},[1,2]]}`\n\tr = bytes.NewBufferString(data)\n\tjm, _ = NewJsonManager(r)\n\n\tq = NewQueryWithString(\".arraytest[0]\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"aaa\":123,\"aab\":234}`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\tassert.Equal([]string{}, c)\n\n\t// case 3-1\n\tdata = `{\"aa\":\"abcde\",\"bb\":{\"foo\":\"bar\"}}`\n\tr = bytes.NewBufferString(data)\n\tjm, _ = NewJsonManager(r)\n\n\tq = NewQueryWithString(\".bb\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"foo\":\"bar\"}`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\tassert.Equal([]string{}, c)\n\n\t// case 4-1\n\tdata = `[{\"name\": \"simeji\"},{\"name\": \"simeji2\"}]`\n\tr = bytes.NewBufferString(data)\n\tjm, _ = NewJsonManager(r)\n\n\tq = NewQueryWithString(\"\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`[{\"name\":\"simeji\"},{\"name\":\"simeji2\"}]`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\tassert.Equal([]string{}, c)\n\n\t// case 5-1\n\tdata = `{\"PrivateName\":\"simei\", \"PrivateAlias\": \"simeji2\"}`\n\tr = bytes.NewBufferString(data)\n\tjm, _ = NewJsonManager(r)\n\n\tq = NewQueryWithString(\".Private\")\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\td, _ = result.Encode()\n\tassert.Equal([]string{``, `Private`}, s)\n\tassert.Equal([]string{\"PrivateAlias\", \"PrivateName\"}, c)\n\n}\n\nfunc TestGetFilteredDataWithMatchQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tdata := `{\"name\":[1,2,3], \"naming\":{\"account\":\"simeji\"}, \"test\":\"simeji\", \"testing\":\"ok\"}`\n\tr := bytes.NewBufferString(data)\n\tjm, _ := NewJsonManager(r)\n\n\tq := NewQueryWithString(`.name`)\n\tresult, s, c, err := jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ := result.Encode()\n\tassert.Equal(`[1,2,3]`, string(d))\n\tassert.Equal([]string{\"[\", \"[\"}, s)\n\tassert.Equal([]string{}, c)\n\n\tq = NewQueryWithString(`.naming`)\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"account\":\"simeji\"}`, string(d))\n\tassert.Equal([]string{\"\", \"\"}, s)\n\tassert.Equal([]string{}, c)\n\n\tq = NewQueryWithString(`.naming.`)\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"account\":\"simeji\"}`, string(d))\n\tassert.Equal([]string{\"account\", \"account\"}, s)\n\tassert.Equal([]string{\"account\"}, c)\n\n\tq = NewQueryWithString(`.test`)\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`{\"name\":[1,2,3],\"naming\":{\"account\":\"simeji\"},\"test\":\"simeji\",\"testing\":\"ok\"}`, string(d))\n\tassert.Equal([]string{\"\", \"test\"}, s)\n\tassert.Equal([]string{\"test\", \"testing\"}, c)\n}\n\nfunc TestGetFilteredDataWithContainDots(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\t// data\n\tdata := `{\"abc.de\":\"2AA2\",\"abcde_fgh\":{\"aaa\":[123,\"cccc\",[1,2]],\"c\":\"JJJJ\"},\"cc\":{\"a\":[3,4]}}`\n\tr := bytes.NewBufferString(data)\n\tjm, _ := NewJsonManager(r)\n\n\t// case 1\n\tq := NewQueryWithString(`.\\\"abc.de\\\"`)\n\tresult, s, c, err := jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ := result.Encode()\n\tassert.Equal(`\"2AA2\"`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\tassert.Equal([]string{}, c)\n\n\t// case 2\n\tq = NewQueryWithString(`.\"abc.de\"`)\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`null`, string(d))\n\tassert.Equal([]string{``, ``}, s)\n\tassert.Equal([]string{}, c)\n\n\t// case 3\n\tq = NewQueryWithString(`.abc.de`)\n\tresult, s, c, err = jm.GetFilteredData(q, false)\n\tassert.Nil(err)\n\td, _ = result.Encode()\n\tassert.Equal(`null`, string(d))\n\tassert.Equal([]string{\"\", \"\"}, s)\n\tassert.Equal([]string{}, c)\n}\n\nfunc TestGetCandidateKeys(t *testing.T) {\n\tvar assert = assert.New(t)\n\tdata := `{\"name\":[1,2,3], \"naming\":{\"account\":\"simeji\"}, \"test\":\"simeji\", \"testing\":\"ok\"}`\n\tr := bytes.NewBufferString(data)\n\tjm, _ := NewJsonManager(r)\n\n\tq := NewQueryWithString(`.n`)\n\n\tkeys := jm.GetCandidateKeys(q)\n\tassert.Equal([]string{\"name\", \"naming\"}, keys)\n\n\tq = NewQueryWithString(`.`)\n\tkeys = jm.GetCandidateKeys(q)\n\tassert.Equal([]string{\"name\", \"naming\", \"test\", \"testing\"}, keys)\n\n\tq = NewQueryWithString(`.test`)\n\tkeys = jm.GetCandidateKeys(q)\n\tassert.Equal([]string{\"test\", \"testing\"}, keys)\n\n\tq = NewQueryWithString(`.testi`)\n\tkeys = jm.GetCandidateKeys(q)\n\tassert.Equal([]string{\"testing\"}, keys)\n\n\tq = NewQueryWithString(`.testia`)\n\tkeys = jm.GetCandidateKeys(q)\n\tassert.Equal([]string{}, keys)\n}\n\nfunc TestGetCurrentKeys(t *testing.T) {\n\tvar assert = assert.New(t)\n\tr := bytes.NewBufferString(`{\"name\":\"go\",\"age\":20,\"weight\":60}`)\n\tbuf, _ := io.ReadAll(r)\n\tsj, _ := simplejson.NewJson(buf)\n\n\tkeys := getCurrentKeys(sj)\n\tassert.Equal([]string{\"age\", \"name\", \"weight\"}, keys)\n\n\tr = bytes.NewBufferString(`[2,3,\"aa\"]`)\n\tbuf, _ = io.ReadAll(r)\n\tsj, _ = simplejson.NewJson(buf)\n\n\tkeys = getCurrentKeys(sj)\n\tassert.Equal([]string{}, keys)\n}\n\nfunc TestIsEmptyJson(t *testing.T) {\n\tvar assert = assert.New(t)\n\tr := bytes.NewBufferString(`{\"name\":\"go\"}`)\n\tbuf, _ := io.ReadAll(r)\n\tsj, _ := simplejson.NewJson(buf)\n\n\tassert.Equal(false, isEmptyJson(sj))\n\tassert.Equal(true, isEmptyJson(&simplejson.Json{}))\n}\n"
  },
  {
    "path": "query.go",
    "content": "package jid\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n\n\trunewidth \"github.com/mattn/go-runewidth\"\n)\n\ntype QueryInterface interface {\n\tGet() []rune\n\tSet(query []rune) []rune\n\tInsert(query []rune, idx int) []rune\n\tAdd(query []rune) []rune\n\tDelete(i int) []rune\n\tClear() []rune\n\tLength() int\n\tIndexOffset(int) int\n\tGetChar(int) rune\n\tGetKeywords() [][]rune\n\tGetLastKeyword() []rune\n\tPopKeyword() ([]rune, []rune)\n\tStringGet() string\n\tStringSet(query string) string\n\tStringInsert(query string, idx int) string\n\tStringAdd(query string) string\n\tStringGetKeywords() []string\n\tStringGetLastKeyword() string\n\tStringPopKeyword() (string, []rune)\n}\n\ntype Query struct {\n\tquery    *[]rune\n\tcomplete *[]rune\n}\n\nfunc NewQuery(query []rune) *Query {\n\tq := &Query{\n\t\tquery:    &[]rune{},\n\t\tcomplete: &[]rune{},\n\t}\n\t_ = q.Set(query)\n\treturn q\n}\nfunc NewQueryWithString(query string) *Query {\n\treturn NewQuery([]rune(query))\n}\n\nfunc (q *Query) Get() []rune {\n\treturn *q.query\n}\n\nfunc (q *Query) GetChar(idx int) rune {\n\tvar r rune = 0\n\tqq := q.Get()\n\tif l := len(qq); l > idx && idx >= 0 {\n\t\tr = qq[idx]\n\t}\n\treturn r\n}\n\nfunc (q *Query) Length() int {\n\treturn len(q.Get())\n}\n\nfunc (q *Query) IndexOffset(i int) int {\n\to := 0\n\tif l := q.Length(); i >= l {\n\t\to = runewidth.StringWidth(q.StringGet())\n\t} else if i >= 0 && i < l {\n\t\to = runewidth.StringWidth(string(q.Get()[:i]))\n\t}\n\treturn o\n}\n\nfunc (q *Query) Set(query []rune) []rune {\n\tif validate(query) {\n\t\tq.query = &query\n\t}\n\treturn q.Get()\n}\n\nfunc (q *Query) Insert(query []rune, idx int) []rune {\n\tqq := q.Get()\n\tif idx == 0 {\n\t\tqq = append(query, qq...)\n\t} else if idx > 0 && len(qq) >= idx {\n\t\t_q := make([]rune, idx+len(query)-1)\n\t\tcopy(_q, qq[:idx])\n\t\tqq = append(append(_q, query...), qq[idx:]...)\n\t}\n\treturn q.Set(qq)\n}\n\nfunc (q *Query) StringInsert(query string, idx int) string {\n\treturn string(q.Insert([]rune(query), idx))\n}\n\nfunc (q *Query) Add(query []rune) []rune {\n\treturn q.Set(append(q.Get(), query...))\n}\n\nfunc (q *Query) Delete(i int) []rune {\n\tvar d []rune\n\tqq := q.Get()\n\tlastIdx := len(qq)\n\tif i < 0 {\n\t\tif lastIdx+i >= 0 {\n\t\t\td = qq[lastIdx+i:]\n\t\t\tqq = qq[0 : lastIdx+i]\n\t\t} else {\n\t\t\td = qq\n\t\t\tqq = qq[0:0]\n\t\t}\n\t} else if i == 0 {\n\t\td = []rune{}\n\t\tqq = qq[1:]\n\t} else if i > 0 && i < lastIdx {\n\t\td = []rune{qq[i]}\n\t\tqq = append(qq[:i], qq[i+1:]...)\n\t}\n\t_ = q.Set(qq)\n\treturn d\n}\n\nfunc (q *Query) Clear() []rune {\n\treturn q.Set([]rune(\"\"))\n}\n\nfunc (q *Query) GetKeywords() [][]rune {\n\tqq := *q.query\n\n\tif qq == nil || string(qq) == \"\" {\n\t\treturn [][]rune{}\n\t}\n\n\tsplitQuery := []string{}\n\trr := []rune{}\n\tenclosed := true\n\tql := len(*q.query)\n\tfor i := 0; i < ql; i++ {\n\t\tr := qq[i]\n\t\tif ii := i + 1; r == '\\\\' && ql > ii && qq[ii] == '\"' {\n\t\t\tenclosed = !enclosed\n\t\t\ti++ // skip '\"(double quortation)'\n\t\t\tcontinue\n\t\t}\n\t\tif enclosed && r == '.' {\n\t\t\tsplitQuery = append(splitQuery, string(rr))\n\t\t\trr = []rune{}\n\t\t} else {\n\t\t\trr = append(rr, r)\n\t\t}\n\t}\n\tif rr != nil {\n\t\tv := []string{string(rr)}\n\t\tif !enclosed {\n\t\t\tv = strings.Split(string(rr), \".\")\n\t\t}\n\t\tsplitQuery = append(splitQuery, v...)\n\t}\n\tlastIdx := len(splitQuery) - 1\n\n\tkeywords := [][]rune{}\n\tfor i, keyword := range splitQuery {\n\t\tif keyword != \"\" || i == lastIdx {\n\t\t\tre := regexp.MustCompile(`\\[[0-9]*\\]?`)\n\t\t\tmatchIndexes := re.FindAllStringIndex(keyword, -1)\n\t\t\tif len(matchIndexes) < 1 {\n\t\t\t\tkeywords = append(keywords, []rune(keyword))\n\t\t\t} else {\n\t\t\t\tif matchIndexes[0][0] > 0 {\n\t\t\t\t\tkeywords = append(keywords, []rune(keyword[0:matchIndexes[0][0]]))\n\t\t\t\t}\n\t\t\t\tfor _, matchIndex := range matchIndexes {\n\t\t\t\t\tk := keyword[matchIndex[0]:matchIndex[1]]\n\t\t\t\t\tkeywords = append(keywords, []rune(k))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn keywords\n}\n\nfunc (q *Query) GetLastKeyword() []rune {\n\tkeywords := q.GetKeywords()\n\tif l := len(keywords); l > 0 {\n\t\treturn keywords[l-1]\n\t}\n\treturn []rune(\"\")\n}\n\nfunc (q *Query) StringGetLastKeyword() string {\n\treturn string(q.GetLastKeyword())\n}\n\nfunc (q *Query) PopKeyword() ([]rune, []rune) {\n\tkeyword := q.GetLastKeyword()\n\tnq := string(keyword)\n\tqq := q.StringGet()\n\n\tfor _, r := range keyword {\n\t\tif r == '.' {\n\t\t\tnq = `\\\"` + string(keyword) + `\\\"`\n\t\t\tbreak\n\t\t}\n\t}\n\tre := regexp.MustCompile(`(\\.)?(\\\\\")?` + regexp.QuoteMeta(nq) + \"$\")\n\n\tqq = re.ReplaceAllString(qq, \"\")\n\n\tquery := q.Set([]rune(qq))\n\treturn keyword, query\n}\n\nfunc (q *Query) StringGet() string {\n\treturn string(q.Get())\n}\n\nfunc (q *Query) StringSet(query string) string {\n\treturn string(q.Set([]rune(query)))\n}\n\nfunc (q *Query) StringAdd(query string) string {\n\treturn string(q.Add([]rune(query)))\n}\n\nfunc (q *Query) StringGetKeywords() []string {\n\tvar keywords []string\n\tfor _, keyword := range q.GetKeywords() {\n\t\tkeywords = append(keywords, string(keyword))\n\t}\n\treturn keywords\n}\n\nfunc (q *Query) StringPopKeyword() (string, []rune) {\n\tkeyword, query := q.PopKeyword()\n\treturn string(keyword), query\n}\n\nfunc validate(r []rune) bool {\n\ts := string(r)\n\tif s == \"\" {\n\t\treturn true\n\t}\n\tif regexp.MustCompile(`^[^.]`).MatchString(s) {\n\t\treturn false\n\t}\n\tif regexp.MustCompile(`\\.{2,}`).MatchString(s) {\n\t\treturn false\n\t}\n\tif regexp.MustCompile(`\\[[0-9]*\\][^\\.\\[]`).MatchString(s) {\n\t\treturn false\n\t}\n\tif regexp.MustCompile(`\\[{2,}|\\]{2,}`).MatchString(s) {\n\t\treturn false\n\t}\n\tif regexp.MustCompile(`.\\.\\[`).MatchString(s) {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "query_test.go",
    "content": "package jid\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestValidate(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tassert.True(validate([]rune(\".test.name\")))\n\tassert.True(validate([]rune(\".test.name.\")))\n\tassert.True(validate([]rune(\".test[0].name.\")))\n\tassert.True(validate([]rune(\".[0].name.\")))\n\tassert.True(validate([]rune(\".name[9][1]\")))\n\tassert.True(validate([]rune(\".[0][1].name.\")))\n\n\tassert.False(validate([]rune(\"[0].name.\")))\n\tassert.False(validate([]rune(\".test[0]].name.\")))\n\tassert.False(validate([]rune(\".test..name\")))\n\tassert.False(validate([]rune(\".test.name..\")))\n\tassert.False(validate([]rune(\".test[[0]].name.\")))\n\tassert.False(validate([]rune(\".test[0]name.\")))\n\tassert.False(validate([]rune(\".test.[0].name.\")))\n}\n\nfunc TestNewQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".name\")\n\tq := NewQuery(v)\n\n\tassert.Equal(*q.query, []rune(\".name\"))\n\tassert.Equal(*q.complete, []rune(\"\"))\n}\n\nfunc TestNewQueryWithInvalidQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\"name\")\n\tq := NewQuery(v)\n\n\tassert.Equal(*q.query, []rune(\"\"))\n\tassert.Equal(*q.complete, []rune(\"\"))\n}\n\nfunc TestNewQueryWithString(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tq := NewQueryWithString(\".name\")\n\n\tassert.Equal(*q.query, []rune(\".name\"))\n\tassert.Equal(*q.complete, []rune(\"\"))\n}\n\nfunc TestNewQueryWithStringWithInvalidQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tq := NewQueryWithString(\"name\")\n\n\tassert.Equal(*q.query, []rune(\"\"))\n\tassert.Equal(*q.complete, []rune(\"\"))\n}\n\nfunc TestQueryGet(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.Get(), []rune(\".test\"))\n}\n\nfunc TestQueryLength(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test\")\n\tq := NewQuery(v)\n\n\tassert.Equal(5, q.Length())\n\n\tv = []rune(\".string.日本語.japan\")\n\tq = NewQuery(v)\n\n\tassert.Equal(17, q.Length())\n}\n\nfunc TestQueryIndexOffsetN(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test\")\n\tq := NewQuery(v)\n\n\tassert.Equal(4, q.IndexOffset(4))\n\tassert.Equal(0, q.IndexOffset(0))\n\tassert.Equal(0, q.IndexOffset(-1))\n\tassert.Equal(5, q.IndexOffset(6))\n\n\t//off-------012345679-101213|j_15,n_19\n\tv = []rune(\".string.日本語.japan\")\n\t//idx-------012345678-9-10|j_12,n_16\n\tq = NewQuery(v)\n\n\tassert.Equal(19, q.IndexOffset(16))\n\tassert.Equal(10, q.IndexOffset(9))\n}\n\nfunc TestQueryGetChar(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test\")\n\tq := NewQuery(v)\n\n\tassert.Equal('e', q.GetChar(2))\n\tassert.Equal('t', q.GetChar(4))\n\tassert.Equal('.', q.GetChar(0))\n\tassert.Equal('.', q.GetChar(0))\n\tassert.Equal(rune(0), q.GetChar(-1))\n\tassert.Equal(rune(0), q.GetChar(6))\n\n\tv = []rune(\".string.日本語.japan\")\n\tq = NewQuery(v)\n\n\tassert.Equal('n', q.GetChar(5))\n\tassert.Equal('本', q.GetChar(9))\n\tassert.Equal('.', q.GetChar(11))\n\tassert.Equal(rune(0), q.GetChar(17))\n}\n\nfunc TestQuerySet(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".hello\")\n\tq := NewQuery(v)\n\n\tassert.Equal([]rune(\".world\"), q.Set([]rune(\".world\")))\n\tassert.Equal(\"\", string(q.Set([]rune(\"\"))))\n}\n\nfunc TestQuerySetWithInvalidQuery(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".hello\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.Set([]rune(\"world\")), []rune(\".hello\"))\n}\n\nfunc TestQueryAdd(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".hello\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.Add([]rune(\"world\")), []rune(\".helloworld\"))\n}\nfunc TestQueryInsert(t *testing.T) {\n\tvar assert = assert.New(t)\n\tv := []rune(\".hello.world\")\n\tq := NewQuery(v)\n\n\tassert.Equal([]rune(\".hello.world\"), q.Insert([]rune(\"w\"), 0))\n\tassert.Equal([]rune(\".whello.world\"), q.Insert([]rune(\"w\"), 1))\n\tassert.Equal([]rune(\".wwhello.world\"), q.Insert([]rune(\"w\"), 1))\n\tassert.Equal([]rune(\".wwhello.world\"), q.Insert([]rune(\".\"), 1))\n\tassert.Equal([]rune(\".wwh.ello.world\"), q.Insert([]rune(\".\"), 4))\n\tassert.Equal([]rune(\".wwh.ello.worldg\"), q.Insert([]rune(\"g\"), 15))\n\tassert.Equal([]rune(\".wwh.ello.worldg\"), q.Insert([]rune(\"a\"), 20))\n}\nfunc TestQueryStringInsert(t *testing.T) {\n\tvar assert = assert.New(t)\n\tq := NewQueryWithString(\".hello.world\")\n\n\tassert.Equal(\".hello.world\", q.StringInsert(\"w\", 0))\n\tassert.Equal(\".whello.world\", q.StringInsert(\"w\", 1))\n\tassert.Equal(\".wwhello.world\", q.StringInsert(\"w\", 1))\n\tassert.Equal(\".wwhello.world\", q.StringInsert(\".\", 1))\n\tassert.Equal(\".wwh.ello.world\", q.StringInsert(\".\", 4))\n\tassert.Equal(\".wwh.ello.worlda\", q.StringInsert(\"a\", 15))\n\tassert.Equal(\".wwh.ello.worlda\", q.StringInsert(\"a\", 20))\n}\n\nfunc TestQueryClear(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.Clear(), []rune(\"\"))\n}\n\nfunc TestQueryDelete(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".helloworld\")\n\tq := NewQuery(v)\n\n\tassert.Equal([]rune(\"d\"), q.Delete(-1))\n\tassert.Equal([]rune(\".helloworl\"), q.Get())\n\tassert.Equal([]rune(\"l\"), q.Delete(-1))\n\tassert.Equal([]rune(\".hellowor\"), q.Get())\n\tassert.Equal([]rune(\"or\"), q.Delete(-2))\n\tassert.Equal([]rune(\".hellow\"), q.Get())\n\tassert.Equal([]rune(\".hellow\"), q.Delete(-8))\n\tassert.Equal([]rune(\"\"), q.Get())\n\n\tq = NewQuery([]rune(\".hello.world\"))\n\tassert.Equal([]rune(\"\"), q.Delete(0))\n\tassert.Equal([]rune(\".hello.world\"), q.Get())\n\tassert.Equal([]rune(\"h\"), q.Delete(1))\n\tassert.Equal([]rune(\".ello.world\"), q.Get())\n\tassert.Equal([]rune(\"e\"), q.Delete(1))\n\tassert.Equal([]rune(\".llo.world\"), q.Get())\n\tassert.Equal([]rune(\"\"), q.Delete(0))\n\tassert.Equal([]rune(\".llo.world\"), q.Get())\n\tassert.Equal([]rune(\"o\"), q.Delete(3))\n\tassert.Equal([]rune(\".ll.world\"), q.Get())\n\tassert.Equal([]rune(\".\"), q.Delete(3))\n\tassert.Equal([]rune(\".llworld\"), q.Get())\n\tassert.Equal([]rune(\"w\"), q.Delete(3))\n\tassert.Equal([]rune(\".llorld\"), q.Get())\n}\n\nfunc TestGetKeywords(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test.name\")\n\tq := NewQuery(v)\n\tassert.Equal([][]rune{\n\t\t[]rune(\"test\"),\n\t\t[]rune(\"name\"),\n\t}, q.GetKeywords())\n\n\tv = []rune(\"\")\n\tq = NewQuery(v)\n\tassert.Equal([][]rune{}, q.GetKeywords())\n\n\tv = []rune(\".test.name.\")\n\tq = NewQuery(v)\n\tassert.Equal([][]rune{\n\t\t[]rune(\"test\"),\n\t\t[]rune(\"name\"),\n\t\t[]rune(\"\"),\n\t}, q.GetKeywords())\n\n\tv = []rune(\".hello\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"hello\"),\n\t})\n\n\tv = []rune(\".hello.\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"hello\"),\n\t\t[]rune(\"\"),\n\t})\n\n\tv = []rune(\".hello[\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"hello\"),\n\t\t[]rune(\"[\"),\n\t})\n\n\tv = []rune(\".hello[12\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"hello\"),\n\t\t[]rune(\"[12\"),\n\t})\n\n\tv = []rune(\".hello[0]\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"hello\"),\n\t\t[]rune(\"[0]\"),\n\t})\n\n\tv = []rune(\".hello[13][0]\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"hello\"),\n\t\t[]rune(\"[13]\"),\n\t\t[]rune(\"[0]\"),\n\t})\n\n\tv = []rune(\".[3][23].hello[13][0]\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetKeywords(), [][]rune{\n\t\t[]rune(\"[3]\"),\n\t\t[]rune(\"[23]\"),\n\t\t[]rune(\"hello\"),\n\t\t[]rune(\"[13]\"),\n\t\t[]rune(\"[0]\"),\n\t})\n\n}\nfunc TestGetKeywordsWithDots(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(`.test.\\\"na.me\\\"`)\n\tq := NewQuery(v)\n\tassert.Equal([][]rune{\n\t\t[]rune(\"test\"),\n\t\t[]rune(\"na.me\"),\n\t}, q.GetKeywords())\n\n\tv = []rune(`.test.\\\"na.me\\`)\n\tq = NewQuery(v)\n\tassert.Equal([][]rune{\n\t\t[]rune(\"test\"),\n\t\t[]rune(\"na\"),\n\t\t[]rune(`me\\`),\n\t}, q.GetKeywords())\n\n}\n\nfunc TestGetLastKeyword(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test.name\")\n\tq := NewQuery(v)\n\tassert.Equal(q.GetLastKeyword(), []rune(\"name\"))\n\n\tv = []rune(\".test.\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetLastKeyword(), []rune(\"\"))\n\n\tv = []rune(\".test\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetLastKeyword(), []rune(\"test\"))\n\n\tv = []rune(\"\")\n\tq = NewQuery(v)\n\tassert.Equal(q.GetLastKeyword(), []rune(\"\"))\n}\n\nfunc TestStringGetLastKeyword(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test.name\")\n\tq := NewQuery(v)\n\tassert.Equal(q.StringGetLastKeyword(), \"name\")\n\n\tv = []rune(\".test.\")\n\tq = NewQuery(v)\n\tassert.Equal(q.StringGetLastKeyword(), \"\")\n\n\tv = []rune(\".test\")\n\tq = NewQuery(v)\n\tassert.Equal(q.StringGetLastKeyword(), \"test\")\n\n\tv = []rune(\"\")\n\tq = NewQuery(v)\n\tassert.Equal(q.StringGetLastKeyword(), \"\")\n}\n\nfunc TestPopKeyword(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test.name\")\n\tq := NewQuery(v)\n\tk, query := q.PopKeyword()\n\tassert.Equal([]rune(\"name\"), k)\n\tassert.Equal([]rune(\".test\"), query)\n\tassert.Equal([]rune(\".test\"), q.Get())\n\n\tv = []rune(\".a[0\")\n\tq = NewQuery(v)\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(\"[0\"), k)\n\tassert.Equal([]rune(\".a\"), query)\n\tassert.Equal([]rune(\".a\"), q.Get())\n\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(\"a\"), k)\n\tassert.Equal([]rune(\"\"), query)\n\tassert.Equal([]rune(\"\"), q.Get())\n\n\tv = []rune(\".\")\n\tq = NewQuery(v)\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(\"\"), k)\n\tassert.Equal([]rune(\"\"), query)\n\tassert.Equal([]rune(\"\"), q.Get())\n\n\tv = []rune(\".test.name.\")\n\tq = NewQuery(v)\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(\"\"), k)\n\tassert.Equal([]rune(\".test.name\"), query)\n\tassert.Equal([]rune(\".test.name\"), q.Get())\n\n\tv = []rune(`.name.\\\"te.st\\\"`)\n\tq = NewQuery(v)\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(\"te.st\"), k)\n\tassert.Equal([]rune(\".name\"), query)\n\tassert.Equal([]rune(\".name\"), q.Get())\n\n\tv = []rune(`.name.\\\"te.st\\\".hoge`)\n\tq = NewQuery(v)\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(\"hoge\"), k)\n\tassert.Equal([]rune(`.name.\\\"te.st\\\"`), query)\n\tassert.Equal([]rune(`.name.\\\"te.st\\\"`), q.Get())\n\n\tv = []rune(`.name.\\\"te`)\n\tq = NewQuery(v)\n\tk, query = q.PopKeyword()\n\tassert.Equal([]rune(`te`), k)\n\tassert.Equal([]rune(`.name`), query)\n\tassert.Equal([]rune(`.name`), q.Get())\n}\n\nfunc TestQueryStringGet(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.StringGet(), \".test\")\n}\n\nfunc TestQueryStringSet(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".hello\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.StringSet(\".world\"), \".world\")\n}\n\nfunc TestQueryStringAdd(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".hello\")\n\tq := NewQuery(v)\n\n\tassert.Equal(q.StringAdd(\"world\"), \".helloworld\")\n}\n\nfunc TestStringGetKeywords(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test.name\")\n\tq := NewQuery(v)\n\tassert.Equal(q.StringGetKeywords(), []string{\n\t\t\"test\",\n\t\t\"name\",\n\t})\n\n\tv = []rune(\".test.name\")\n\tq = NewQuery(v)\n\tassert.Equal(q.StringGetKeywords(), []string{\n\t\t\"test\",\n\t\t\"name\",\n\t})\n\n\tv = []rune(\"\")\n\tq = NewQuery(v)\n\tkws := q.StringGetKeywords()\n\tassert.Equal([]string(nil), kws)\n\tassert.Equal(0, len(kws))\n}\n\nfunc TestStringPopKeyword(t *testing.T) {\n\tvar assert = assert.New(t)\n\n\tv := []rune(\".test.name\")\n\tq := NewQuery(v)\n\tk, query := q.StringPopKeyword()\n\tassert.Equal(k, \"name\")\n\tassert.Equal(query, []rune(\".test\"))\n\tassert.Equal(q.Get(), []rune(\".test\"))\n\n\tv = []rune(\".test.name.\")\n\tq = NewQuery(v)\n\tk, query = q.StringPopKeyword()\n\tassert.Equal(k, \"\")\n\tassert.Equal(query, []rune(\".test.name\"))\n\tassert.Equal(q.Get(), []rune(\".test.name\"))\n\n\tv = []rune(\".test.name[23]\")\n\tq = NewQuery(v)\n\tk, query = q.StringPopKeyword()\n\tassert.Equal(k, \"[23]\")\n\tassert.Equal(query, []rune(\".test.name\"))\n\tassert.Equal(q.Get(), []rune(\".test.name\"))\n}\n"
  },
  {
    "path": "suggestion.go",
    "content": "package jid\n\nimport (\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\n\tsimplejson \"github.com/bitly/go-simplejson\"\n)\n\ntype SuggestionInterface interface {\n\tGet(json *simplejson.Json, keyword string) []string\n\tGetCandidateKeys(json *simplejson.Json, keyword string) []string\n}\n\ntype SuggestionDataType int\n\nconst (\n\tUNKNOWN SuggestionDataType = iota\n\tARRAY\n\tMAP\n\tNUMBER\n\tSTRING\n\tBOOL\n)\n\ntype Suggestion struct {\n}\n\nfunc NewSuggestion() *Suggestion {\n\treturn &Suggestion{}\n}\n\nfunc (s *Suggestion) Get(json *simplejson.Json, keyword string) []string {\n\tvar completion string\n\tvar suggestion string\n\n\tif a, err := json.Array(); err == nil {\n\t\tif len(a) > 1 {\n\t\t\tkw := regexp.MustCompile(`\\[([0-9]+)?\\]?`).FindString(keyword)\n\t\t\tif kw == \"\" {\n\t\t\t\treturn []string{\"[\", \"[\"}\n\t\t\t} else if kw == \"[\" {\n\t\t\t\treturn []string{\"\", \"[\"}\n\t\t\t}\n\t\t\treturn []string{strings.Replace(kw+\"]\", kw, \"\", -1), kw + \"]\"}\n\t\t}\n\t\treturn []string{strings.Replace(`[0]`, keyword, \"\", -1), `[0]`}\n\t}\n\n\tcandidateKeys := s.GetCandidateKeys(json, keyword)\n\n\tif keyword == \"\" {\n\t\tif l := len(candidateKeys); l > 1 {\n\t\t\treturn []string{\"\", \"\"}\n\t\t} else if l == 1 {\n\t\t\treturn []string{candidateKeys[0], candidateKeys[0]}\n\t\t}\n\t}\n\n\tfor _, key := range candidateKeys {\n\t\t// first\n\t\tif suggestion == \"\" && key != \"\" {\n\t\t\tsuggestion = key\n\t\t} else {\n\t\t\taxis := suggestion\n\t\t\tif len(suggestion) > len(key) {\n\t\t\t\taxis = key\n\t\t\t}\n\t\t\tmax := 0\n\t\t\tfor i, _ := range axis {\n\t\t\t\tif suggestion[i] != key[i] {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tmax = i\n\t\t\t}\n\t\t\tif max == 0 {\n\t\t\t\tsuggestion = \"\"\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tsuggestion = suggestion[0 : max+1]\n\t\t}\n\t}\n\tif reg, err := regexp.Compile(\"(?i)^\" + keyword); err == nil {\n\t\tcompletion = reg.ReplaceAllString(suggestion, \"\")\n\t}\n\treturn []string{completion, suggestion}\n}\n\nfunc (s *Suggestion) GetCandidateKeys(json *simplejson.Json, keyword string) []string {\n\tcandidates := []string{}\n\n\tif _, err := json.Array(); err == nil {\n\t\treturn []string{}\n\t}\n\n\tif keyword == \"\" {\n\t\treturn getCurrentKeys(json)\n\t}\n\n\treg, err := regexp.Compile(`(?i)^(\\\\\")?` + keyword + `(\\\\\")?`)\n\tif err != nil {\n\t\treturn []string{}\n\t}\n\tfor _, key := range getCurrentKeys(json) {\n\t\tif reg.MatchString(key) {\n\t\t\tcandidates = append(candidates, key)\n\t\t}\n\t}\n\treturn candidates\n}\n\nfunc getCurrentKeys(json *simplejson.Json) []string {\n\n\tkk := []string{}\n\tm, err := json.Map()\n\n\tif err != nil {\n\t\treturn kk\n\t}\n\tfor k := range m {\n\t\tkk = append(kk, k)\n\t}\n\tsort.Strings(kk)\n\n\tkeys := []string{}\n\tfor _, k := range kk {\n\t\tif strings.Contains(k, \".\") {\n\t\t\tvar sb strings.Builder\n\t\t\tsb.Grow(len(k) + 4)\n\t\t\tsb.WriteString(`\\\"`)\n\t\t\tsb.WriteString(k)\n\t\t\tsb.WriteString(`\\\"`)\n\t\t\tk = sb.String()\n\t\t}\n\t\tkeys = append(keys, k)\n\t}\n\treturn keys\n}\n\nfunc (s *Suggestion) GetCurrentType(json *simplejson.Json) SuggestionDataType {\n\tif _, err := json.Array(); err == nil {\n\t\treturn ARRAY\n\t} else if _, err = json.Map(); err == nil {\n\t\treturn MAP\n\t} else if _, err = json.String(); err == nil {\n\t\treturn STRING\n\t}\n\treturn UNKNOWN\n}\n"
  },
  {
    "path": "suggestion_test.go",
    "content": "package jid\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"testing\"\n\n\tsimplejson \"github.com/bitly/go-simplejson\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewSuggestion(t *testing.T) {\n\tvar assert = assert.New(t)\n\tassert.Equal(NewSuggestion(), &Suggestion{})\n}\n\nfunc TestSuggestionGet(t *testing.T) {\n\tvar assert = assert.New(t)\n\tj := createJson(`{\"name\":\"simeji-github\"}`)\n\ts := NewSuggestion()\n\n\tj = createJson(`{\"name\":\"simeji-github\", \"naming\":\"simeji\", \"nickname\":\"simejisimeji\"}`)\n\tassert.Equal([]string{\"m\", \"nam\"}, s.Get(j, \"na\"))\n\n\tj = createJson(`{\"abcde\":\"simeji-github\", \"abcdef\":\"simeji\", \"ab\":\"simejisimeji\"}`)\n\tassert.Equal([]string{\"\", \"\"}, s.Get(j, \"\"))\n\tassert.Equal([]string{\"b\", \"ab\"}, s.Get(j, \"a\"))\n\tassert.Equal([]string{\"de\", \"abcde\"}, s.Get(j, \"abc\"))\n\tassert.Equal([]string{\"\", \"abcde\"}, s.Get(j, \"abcde\"))\n\n\tj = createJson(`[\"zero\"]`)\n\tassert.Equal([]string{\"[0]\", \"[0]\"}, s.Get(j, \"\"))\n\tassert.Equal([]string{\"0]\", \"[0]\"}, s.Get(j, \"[\"))\n\tassert.Equal([]string{\"]\", \"[0]\"}, s.Get(j, \"[0\"))\n\n\tj = createJson(`[\"zero\", \"one\"]`)\n\tassert.Equal([]string{\"[\", \"[\"}, s.Get(j, \"\"))\n\tassert.Equal([]string{\"\", \"[\"}, s.Get(j, \"[\"))\n\tassert.Equal([]string{\"]\", \"[0]\"}, s.Get(j, \"[0\"))\n\n\tj = createJson(`{\"Abcabc\":\"simeji-github\", \"Abcdef\":\"simeji\"}`)\n\tassert.Equal([]string{\"bc\", \"Abc\"}, s.Get(j, \"a\"))\n\tassert.Equal([]string{\"c\", \"Abc\"}, s.Get(j, \"ab\"))\n\n\tj = createJson(`{\"RootDeviceNames\":\"simeji-github\", \"RootDeviceType\":\"simeji\"}`)\n\tassert.Equal([]string{\"ootDevice\", \"RootDevice\"}, s.Get(j, \"r\"))\n\tassert.Equal([]string{\"ootDevice\", \"RootDevice\"}, s.Get(j, \"R\"))\n}\n\nfunc TestSuggestionGetCurrentType(t *testing.T) {\n\tvar assert = assert.New(t)\n\ts := NewSuggestion()\n\n\tj := createJson(`[1,2,3]`)\n\tassert.Equal(ARRAY, s.GetCurrentType(j))\n\tj = createJson(`{\"name\":[1,2,3], \"naming\":{\"account\":\"simeji\"}, \"test\":\"simeji\", \"testing\":\"ok\"}`)\n\tassert.Equal(MAP, s.GetCurrentType(j))\n\tj = createJson(`\"name\"`)\n\tassert.Equal(STRING, s.GetCurrentType(j))\n\tj = createJson(\"1\")\n\tassert.Equal(UNKNOWN, s.GetCurrentType(j))\n}\n\nfunc TestSuggestionGetCandidateKeys(t *testing.T) {\n\tvar assert = assert.New(t)\n\tj := createJson(`{\"naming\":\"simeji\", \"nickname\":\"simejisimeji\", \"city\":\"tokyo\", \"name\":\"simeji-github\" }`)\n\ts := NewSuggestion()\n\n\tassert.Equal([]string{\"city\", \"name\", \"naming\", \"nickname\"}, s.GetCandidateKeys(j, \"\"))\n\tassert.Equal([]string{\"name\", \"naming\", \"nickname\"}, s.GetCandidateKeys(j, \"n\"))\n\tassert.Equal([]string{\"name\", \"naming\"}, s.GetCandidateKeys(j, \"na\"))\n\tassert.Equal([]string{}, s.GetCandidateKeys(j, \"nana\"))\n\n\tj = createJson(`{\"abcde\":\"simeji-github\", \"abcdef\":\"simeji\", \"ab\":\"simejisimeji\"}`)\n\tassert.Equal([]string{\"abcde\", \"abcdef\"}, s.GetCandidateKeys(j, \"abcde\"))\n\n\tj = createJson(`{\"name\":\"simeji-github\"}`)\n\tassert.Equal([]string{\"name\"}, s.GetCandidateKeys(j, \"\"))\n\n\tj = createJson(`{\"n\":\"simeji-github\"}`)\n\tassert.Equal([]string{\"n\"}, s.GetCandidateKeys(j, \"\"))\n\n\tj = createJson(`[1,2,\"aa\"]`)\n\ts = NewSuggestion()\n\tassert.Equal([]string{}, s.GetCandidateKeys(j, \"[\"))\n}\nfunc TestSuggestionGetCandidateKeysWithDots(t *testing.T) {\n\tvar assert = assert.New(t)\n\tj := createJson(`{\"nam.ing\":\"simeji\", \"nickname\":\"simejisimeji\", \"city\":\"tokyo\", \"name\":\"simeji-github\" }`)\n\ts := NewSuggestion()\n\n\tassert.Equal([]string{\"city\", `\\\"nam.ing\\\"`, \"name\", \"nickname\"}, s.GetCandidateKeys(j, \"\"))\n\tassert.Equal([]string{`\\\"nam.ing\\\"`, \"name\", \"nickname\"}, s.GetCandidateKeys(j, \"n\"))\n}\n\nfunc createJson(s string) *simplejson.Json {\n\tr := bytes.NewBufferString(s)\n\tbuf, _ := io.ReadAll(r)\n\tj, _ := simplejson.NewJson(buf)\n\treturn j\n}\n"
  },
  {
    "path": "terminal.go",
    "content": "package jid\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"regexp\"\n\t\"strings\"\n\n\trunewidth \"github.com/mattn/go-runewidth\"\n\ttermbox \"github.com/nsf/termbox-go\"\n\t\"github.com/nwidger/jsoncolor\"\n)\n\ntype Terminal struct {\n\tdefaultY   int\n\tprompt     string\n\tformatter  *jsoncolor.Formatter\n\tmonochrome bool\n\toutputArea *[][]termbox.Cell\n}\n\ntype TerminalDrawAttributes struct {\n\tQuery           string\n\tContents        []string\n\tCandidateIndex  int\n\tContentsOffsetY int\n\tComplete        string\n\tCandidates      []string\n\tCursorOffset    int\n}\n\nfunc NewTerminal(prompt string, defaultY int, monochrome bool) *Terminal {\n\tt := &Terminal{\n\t\tprompt:     prompt,\n\t\tdefaultY:   defaultY,\n\t\tmonochrome: monochrome,\n\t\toutputArea: &[][]termbox.Cell{},\n\t\tformatter:  nil,\n\t}\n\tif !monochrome {\n\t\tt.formatter = t.initColorizeFormatter()\n\t}\n\treturn t\n}\n\nfunc (t *Terminal) Draw(attr *TerminalDrawAttributes) error {\n\n\tquery := attr.Query\n\tcomplete := attr.Complete\n\trows := attr.Contents\n\tcandidates := attr.Candidates\n\tcandidateidx := attr.CandidateIndex\n\tcontentOffsetY := attr.ContentsOffsetY\n\n\ttermbox.Clear(termbox.ColorDefault, termbox.ColorDefault)\n\n\ty := t.defaultY\n\t_, h := termbox.Size()\n\n\tt.drawFilterLine(query, complete)\n\n\tif len(candidates) > 0 {\n\t\ty = t.drawCandidates(0, t.defaultY, candidateidx, candidates)\n\t}\n\n\tcellsArr, err := t.rowsToCells(rows)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor idx, cells := range cellsArr {\n\t\ti := idx - contentOffsetY\n\t\tif i >= 0 {\n\t\t\tt.drawCells(0, i+y, cells)\n\t\t}\n\t\tif i > h {\n\t\t\tbreak\n\t\t}\n\t}\n\n\ttermbox.SetCursor(len(t.prompt)+attr.CursorOffset, 0)\n\n\ttermbox.Flush()\n\treturn nil\n}\n\nfunc (t *Terminal) drawFilterLine(qs string, complete string) error {\n\tfs := t.prompt + qs\n\tcs := complete\n\tstr := fs + cs\n\n\tcolor := termbox.ColorDefault\n\tbackgroundColor := termbox.ColorDefault\n\n\tvar cells []termbox.Cell\n\tmatch := []int{len(fs), len(fs + cs)}\n\n\tvar c termbox.Attribute\n\tfor i, s := range str {\n\t\tc = color\n\t\tif i >= match[0] && i < match[1] {\n\t\t\tc = termbox.ColorGreen\n\t\t}\n\t\tcells = append(cells, termbox.Cell{\n\t\t\tCh: s,\n\t\t\tFg: c,\n\t\t\tBg: backgroundColor,\n\t\t})\n\t}\n\tt.drawCells(0, 0, cells)\n\treturn nil\n}\n\ntype termboxSprintfFuncer struct {\n\tfg         termbox.Attribute\n\tbg         termbox.Attribute\n\toutputArea *[][]termbox.Cell\n}\n\nfunc (tsf *termboxSprintfFuncer) SprintfFunc() func(format string, a ...interface{}) string {\n\treturn func(format string, a ...interface{}) string {\n\t\tcells := tsf.outputArea\n\t\tidx := len(*cells) - 1\n\t\tstr := fmt.Sprintf(format, a...)\n\t\tfor _, s := range str {\n\t\t\tif s == '\\n' {\n\t\t\t\t*cells = append(*cells, []termbox.Cell{})\n\t\t\t\tidx++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t(*cells)[idx] = append((*cells)[idx], termbox.Cell{\n\t\t\t\tCh: s,\n\t\t\t\tFg: tsf.fg,\n\t\t\t\tBg: tsf.bg,\n\t\t\t})\n\t\t}\n\t\treturn \"dummy\"\n\t}\n}\n\nfunc (t *Terminal) initColorizeFormatter() *jsoncolor.Formatter {\n\tformatter := jsoncolor.NewFormatter()\n\n\tregular := &termboxSprintfFuncer{\n\t\tfg:         termbox.ColorDefault,\n\t\tbg:         termbox.ColorDefault,\n\t\toutputArea: t.outputArea,\n\t}\n\n\tbold := &termboxSprintfFuncer{\n\t\tfg:         termbox.AttrBold,\n\t\tbg:         termbox.ColorDefault,\n\t\toutputArea: t.outputArea,\n\t}\n\n\tblueBold := &termboxSprintfFuncer{\n\t\tfg:         termbox.ColorBlue | termbox.AttrBold,\n\t\tbg:         termbox.ColorDefault,\n\t\toutputArea: t.outputArea,\n\t}\n\n\tgreen := &termboxSprintfFuncer{\n\t\tfg:         termbox.ColorGreen,\n\t\tbg:         termbox.ColorDefault,\n\t\toutputArea: t.outputArea,\n\t}\n\n\tblackBold := &termboxSprintfFuncer{\n\t\tfg:         termbox.ColorBlack | termbox.AttrBold,\n\t\tbg:         termbox.ColorDefault,\n\t\toutputArea: t.outputArea,\n\t}\n\n\tformatter.SpaceColor = regular\n\tformatter.CommaColor = bold\n\tformatter.ColonColor = bold\n\tformatter.ObjectColor = bold\n\tformatter.ArrayColor = bold\n\tformatter.FieldQuoteColor = blueBold\n\tformatter.FieldColor = blueBold\n\tformatter.StringQuoteColor = green\n\tformatter.StringColor = green\n\tformatter.TrueColor = regular\n\tformatter.FalseColor = regular\n\tformatter.NumberColor = regular\n\tformatter.NullColor = blackBold\n\n\treturn formatter\n}\n\nfunc (t *Terminal) rowsToCells(rows []string) ([][]termbox.Cell, error) {\n\t*t.outputArea = [][]termbox.Cell{[]termbox.Cell{}}\n\n\tvar err error\n\n\tif t.formatter != nil {\n\t\terr = t.formatter.Format(io.Discard, []byte(strings.Join(rows, \"\\n\")))\n\t}\n\n\tcells := *t.outputArea\n\n\tif err != nil || t.monochrome {\n\t\tcells = [][]termbox.Cell{}\n\t\tfor _, row := range rows {\n\t\t\tvar cls []termbox.Cell\n\t\t\tfor _, char := range row {\n\t\t\t\tcls = append(cls, termbox.Cell{\n\t\t\t\t\tCh: char,\n\t\t\t\t\tFg: termbox.ColorDefault,\n\t\t\t\t\tBg: termbox.ColorDefault,\n\t\t\t\t})\n\t\t\t}\n\t\t\tcells = append(cells, cls)\n\t\t}\n\t}\n\n\treturn cells, nil\n}\n\nfunc (t *Terminal) drawCells(x int, y int, cells []termbox.Cell) {\n\ti := 0\n\tfor _, c := range cells {\n\t\ttermbox.SetCell(x+i, y, c.Ch, c.Fg, c.Bg)\n\n\t\tw := runewidth.RuneWidth(c.Ch)\n\t\tif w == 0 || w == 2 && runewidth.IsAmbiguousWidth(c.Ch) {\n\t\t\tw = 1\n\t\t}\n\n\t\ti += w\n\t}\n}\n\nfunc (t *Terminal) drawCandidates(x int, y int, index int, candidates []string) int {\n\tcolor := termbox.ColorBlack\n\tbackgroundColor := termbox.ColorWhite\n\n\tw, _ := termbox.Size()\n\n\tss := candidates[index]\n\tre := regexp.MustCompile(\"[[:space:]]\" + regexp.QuoteMeta(ss) + \"[[:space:]]\")\n\n\tvar rows []string\n\tvar str string\n\tfor _, word := range candidates {\n\t\tcombine := \" \"\n\t\tif l := len(str); l+len(word)+1 >= w {\n\t\t\trows = append(rows, str+\" \")\n\t\t\tstr = \"\"\n\t\t}\n\t\tstr += combine + word\n\t}\n\trows = append(rows, str+\" \")\n\n\tfor i, row := range rows {\n\t\tmatch := re.FindStringIndex(row)\n\t\tvar c termbox.Attribute\n\t\tii := 0\n\t\tfor k, s := range row {\n\t\t\tc = color\n\t\t\tbackgroundColor = termbox.ColorMagenta\n\t\t\tif match != nil && k >= match[0]+1 && k < match[1]-1 {\n\t\t\t\tbackgroundColor = termbox.ColorWhite\n\t\t\t}\n\t\t\ttermbox.SetCell(x+ii, y+i, s, c, backgroundColor)\n\t\t\tw := runewidth.RuneWidth(s)\n\t\t\tif w == 0 || w == 2 && runewidth.IsAmbiguousWidth(s) {\n\t\t\t\tw = 1\n\t\t\t}\n\t\t\tii += w\n\t\t}\n\t}\n\treturn y + len(rows)\n}\n"
  }
]