diff --git a/json_manager_test.go b/json_manager_test.go index 71f959a..dedbc27 100644 --- a/json_manager_test.go +++ b/json_manager_test.go @@ -2,10 +2,11 @@ package jid import ( "bytes" - "github.com/bitly/go-simplejson" - "github.com/stretchr/testify/assert" "io/ioutil" "testing" + + simplejson "github.com/bitly/go-simplejson" + "github.com/stretchr/testify/assert" ) func TestNewJson(t *testing.T) { @@ -185,6 +186,15 @@ func TestGetItem(t *testing.T) { result, _ = d.Encode() assert.Equal(`{"age":20,"name":"go"}`, string(result)) + // case 7 key contains '.' + rr = bytes.NewBufferString(`{"na.me":"go","age":20}`) + buf, _ = ioutil.ReadAll(rr) + sj, _ = simplejson.NewJson(buf) + + d, _ = getItem(sj, "na.me") + result, _ = d.Encode() + assert.Equal(`"go"`, string(result)) + } func TestGetFilteredData(t *testing.T) { @@ -363,6 +373,42 @@ func TestGetFilteredDataWithMatchQuery(t *testing.T) { assert.Equal([]string{"test", "testing"}, c) } +func TestGetFilteredDataWithContainDots(t *testing.T) { + var assert = assert.New(t) + + // data + data := `{"abc.de":"2AA2","abcde_fgh":{"aaa":[123,"cccc",[1,2]],"c":"JJJJ"},"cc":{"a":[3,4]}}` + r := bytes.NewBufferString(data) + jm, _ := NewJsonManager(r) + + // case 1 + q := NewQueryWithString(`.\"abc.de\"`) + result, s, c, err := jm.GetFilteredData(q, false) + assert.Nil(err) + d, _ := result.Encode() + assert.Equal(`"2AA2"`, string(d)) + assert.Equal([]string{``, ``}, s) + assert.Equal([]string{}, c) + + // case 2 + q = NewQueryWithString(`."abc.de"`) + result, s, c, err = jm.GetFilteredData(q, false) + assert.Nil(err) + d, _ = result.Encode() + assert.Equal(`null`, string(d)) + assert.Equal([]string{``, ``}, s) + assert.Equal([]string{}, c) + + // case 3 + q = NewQueryWithString(`.abc.de`) + result, s, c, err = jm.GetFilteredData(q, false) + assert.Nil(err) + d, _ = result.Encode() + assert.Equal(`null`, string(d)) + assert.Equal([]string{"", ""}, s) + assert.Equal([]string{}, c) +} + func TestGetCandidateKeys(t *testing.T) { var assert = assert.New(t) data := `{"name":[1,2,3], "naming":{"account":"simeji"}, "test":"simeji", "testing":"ok"}` diff --git a/query.go b/query.go index 00ec199..482ccca 100644 --- a/query.go +++ b/query.go @@ -4,7 +4,7 @@ import ( "regexp" "strings" - "github.com/mattn/go-runewidth" + runewidth "github.com/mattn/go-runewidth" ) type QueryInterface interface { @@ -128,13 +128,37 @@ func (q *Query) Clear() []rune { } func (q *Query) GetKeywords() [][]rune { - query := string(*q.query) + qq := *q.query - if query == "" { + if qq == nil || string(qq) == "" { return [][]rune{} } - splitQuery := strings.Split(query, ".") + splitQuery := []string{} + rr := []rune{} + enclosed := true + ql := len(*q.query) + for i := 0; i < ql; i++ { + r := qq[i] + if ii := i + 1; r == '\\' && ql > ii && qq[ii] == '"' { + enclosed = !enclosed + i++ // skip '"(double quortation)' + continue + } + if enclosed && r == '.' { + splitQuery = append(splitQuery, string(rr)) + rr = []rune{} + } else { + rr = append(rr, r) + } + } + if rr != nil { + v := []string{string(rr)} + if !enclosed { + v = strings.Split(string(rr), ".") + } + splitQuery = append(splitQuery, v...) + } lastIdx := len(splitQuery) - 1 keywords := [][]rune{} @@ -171,27 +195,21 @@ func (q *Query) StringGetLastKeyword() string { } func (q *Query) PopKeyword() ([]rune, []rune) { - var keyword []rune - var lastSepIdx int - var lastBracketIdx int - qq := q.Get() - for i, e := range qq { - if e == '.' { - lastSepIdx = i - } else if e == '[' { - lastBracketIdx = i + keyword := q.GetLastKeyword() + nq := string(keyword) + qq := q.StringGet() + + for _, r := range keyword { + if r == '.' { + nq = `\"` + string(keyword) + `\"` + break } } + re := regexp.MustCompile(`(\.)?(\\")?` + regexp.QuoteMeta(nq) + "$") - if lastBracketIdx > lastSepIdx { - lastSepIdx = lastBracketIdx - } + qq = re.ReplaceAllString(qq, "") - keywords := q.GetKeywords() - if l := len(keywords); l > 0 { - keyword = keywords[l-1] - } - query := q.Set(qq[0:lastSepIdx]) + query := q.Set([]rune(qq)) return keyword, query } diff --git a/query_test.go b/query_test.go index d411dac..5c14fab 100644 --- a/query_test.go +++ b/query_test.go @@ -1,8 +1,9 @@ package jid import ( - "github.com/stretchr/testify/assert" "testing" + + "github.com/stretchr/testify/assert" ) func TestValidate(t *testing.T) { @@ -226,22 +227,22 @@ func TestGetKeywords(t *testing.T) { v := []rune(".test.name") q := NewQuery(v) - assert.Equal(q.GetKeywords(), [][]rune{ + assert.Equal([][]rune{ []rune("test"), []rune("name"), - }) + }, q.GetKeywords()) v = []rune("") q = NewQuery(v) - assert.Equal(q.GetKeywords(), [][]rune{}) + assert.Equal([][]rune{}, q.GetKeywords()) v = []rune(".test.name.") q = NewQuery(v) - assert.Equal(q.GetKeywords(), [][]rune{ + assert.Equal([][]rune{ []rune("test"), []rune("name"), []rune(""), - }) + }, q.GetKeywords()) v = []rune(".hello") q = NewQuery(v) @@ -295,6 +296,25 @@ func TestGetKeywords(t *testing.T) { []rune("[0]"), }) +} +func TestGetKeywordsWithDots(t *testing.T) { + var assert = assert.New(t) + + v := []rune(`.test.\"na.me\"`) + q := NewQuery(v) + assert.Equal([][]rune{ + []rune("test"), + []rune("na.me"), + }, q.GetKeywords()) + + v = []rune(`.test.\"na.me\`) + q = NewQuery(v) + assert.Equal([][]rune{ + []rune("test"), + []rune("na"), + []rune(`me\`), + }, q.GetKeywords()) + } func TestGetLastKeyword(t *testing.T) { @@ -343,16 +363,56 @@ func TestPopKeyword(t *testing.T) { v := []rune(".test.name") q := NewQuery(v) k, query := q.PopKeyword() - assert.Equal(k, []rune("name")) - assert.Equal(query, []rune(".test")) - assert.Equal(q.Get(), []rune(".test")) + assert.Equal([]rune("name"), k) + assert.Equal([]rune(".test"), query) + assert.Equal([]rune(".test"), q.Get()) + + v = []rune(".a[0") + q = NewQuery(v) + k, query = q.PopKeyword() + assert.Equal([]rune("[0"), k) + assert.Equal([]rune(".a"), query) + assert.Equal([]rune(".a"), q.Get()) + + k, query = q.PopKeyword() + assert.Equal([]rune("a"), k) + assert.Equal([]rune(""), query) + assert.Equal([]rune(""), q.Get()) + + v = []rune(".") + q = NewQuery(v) + k, query = q.PopKeyword() + assert.Equal([]rune(""), k) + assert.Equal([]rune(""), query) + assert.Equal([]rune(""), q.Get()) v = []rune(".test.name.") q = NewQuery(v) k, query = q.PopKeyword() - assert.Equal(k, []rune("")) - assert.Equal(query, []rune(".test.name")) - assert.Equal(q.Get(), []rune(".test.name")) + assert.Equal([]rune(""), k) + assert.Equal([]rune(".test.name"), query) + assert.Equal([]rune(".test.name"), q.Get()) + + v = []rune(`.name.\"te.st\"`) + q = NewQuery(v) + k, query = q.PopKeyword() + assert.Equal([]rune("te.st"), k) + assert.Equal([]rune(".name"), query) + assert.Equal([]rune(".name"), q.Get()) + + v = []rune(`.name.\"te.st\".hoge`) + q = NewQuery(v) + k, query = q.PopKeyword() + assert.Equal([]rune("hoge"), k) + assert.Equal([]rune(`.name.\"te.st\"`), query) + assert.Equal([]rune(`.name.\"te.st\"`), q.Get()) + + v = []rune(`.name.\"te`) + q = NewQuery(v) + k, query = q.PopKeyword() + assert.Equal([]rune(`te`), k) + assert.Equal([]rune(`.name`), query) + assert.Equal([]rune(`.name`), q.Get()) } func TestQueryStringGet(t *testing.T) { diff --git a/suggestion.go b/suggestion.go index 461483b..3ac4881 100644 --- a/suggestion.go +++ b/suggestion.go @@ -1,10 +1,11 @@ package jid import ( - "github.com/bitly/go-simplejson" "regexp" "sort" "strings" + + simplejson "github.com/bitly/go-simplejson" ) type SuggestionInterface interface { @@ -97,7 +98,7 @@ func (s *Suggestion) GetCandidateKeys(json *simplejson.Json, keyword string) []s return getCurrentKeys(json) } - reg, err := regexp.Compile("(?i)^" + keyword) + reg, err := regexp.Compile(`(?i)^(\\")?` + keyword + `(\\")?`) if err != nil { return []string{} } @@ -111,16 +112,29 @@ func (s *Suggestion) GetCandidateKeys(json *simplejson.Json, keyword string) []s func getCurrentKeys(json *simplejson.Json) []string { - keys := []string{} + kk := []string{} m, err := json.Map() if err != nil { - return keys + return kk } for k := range m { + kk = append(kk, k) + } + sort.Strings(kk) + + keys := []string{} + for _, k := range kk { + if strings.Contains(k, ".") { + var sb strings.Builder + sb.Grow(len(k) + 4) + sb.WriteString(`\"`) + sb.WriteString(k) + sb.WriteString(`\"`) + k = sb.String() + } keys = append(keys, k) } - sort.Strings(keys) return keys } diff --git a/suggestion_test.go b/suggestion_test.go index 8193f55..7ac13d0 100644 --- a/suggestion_test.go +++ b/suggestion_test.go @@ -2,10 +2,11 @@ package jid import ( "bytes" - "github.com/bitly/go-simplejson" - "github.com/stretchr/testify/assert" "io/ioutil" "testing" + + simplejson "github.com/bitly/go-simplejson" + "github.com/stretchr/testify/assert" ) func TestNewSuggestion(t *testing.T) { @@ -83,6 +84,14 @@ func TestSuggestionGetCandidateKeys(t *testing.T) { s = NewSuggestion() assert.Equal([]string{}, s.GetCandidateKeys(j, "[")) } +func TestSuggestionGetCandidateKeysWithDots(t *testing.T) { + var assert = assert.New(t) + j := createJson(`{"nam.ing":"simeji", "nickname":"simejisimeji", "city":"tokyo", "name":"simeji-github" }`) + s := NewSuggestion() + + assert.Equal([]string{"city", `\"nam.ing\"`, "name", "nickname"}, s.GetCandidateKeys(j, "")) + assert.Equal([]string{`\"nam.ing\"`, "name", "nickname"}, s.GetCandidateKeys(j, "n")) +} func createJson(s string) *simplejson.Json { r := bytes.NewBufferString(s) diff --git a/terminal.go b/terminal.go index b4f0a20..a56dc22 100644 --- a/terminal.go +++ b/terminal.go @@ -238,7 +238,7 @@ func (t *Terminal) drawCandidates(x int, y int, index int, candidates []string) w, _ := termbox.Size() ss := candidates[index] - re := regexp.MustCompile("[[:space:]]" + ss + "[[:space:]]") + re := regexp.MustCompile("[[:space:]]" + regexp.QuoteMeta(ss) + "[[:space:]]") var rows []string var str string