From 09200ef2a868be1d9104ddf10cde5df637fb0b2f Mon Sep 17 00:00:00 2001 From: William Snyders Date: Mon, 27 Nov 2023 06:21:33 -0800 Subject: [PATCH] add initial query and select one options (#185) Co-authored-by: William Snyders --- fuzzyfinder.go | 12 ++ fuzzyfinder_test.go | 107 ++++++++++++++++++ option.go | 16 +++ ...estfind_withquery-has_initial_query.golden | 11 ++ ...testfind_withquery-no_initial_query.golden | 11 ++ ...ind_withselectone-has_initial_query.golden | 11 ++ ...estfind_withselectone-more_than_one.golden | 11 ++ ...tfind_withselectone-only_one_option.golden | 11 ++ 8 files changed, 190 insertions(+) create mode 100644 testdata/fixtures/testfind_withquery-has_initial_query.golden create mode 100644 testdata/fixtures/testfind_withquery-no_initial_query.golden create mode 100644 testdata/fixtures/testfind_withselectone-has_initial_query.golden create mode 100644 testdata/fixtures/testfind_withselectone-more_than_one.golden create mode 100644 testdata/fixtures/testfind_withselectone-only_one_option.golden diff --git a/fuzzyfinder.go b/fuzzyfinder.go index 78e77da..7b1330b 100644 --- a/fuzzyfinder.go +++ b/fuzzyfinder.go @@ -129,6 +129,14 @@ func (f *finder) initFinder(items []string, matched []matching.Matched, opt opt) f.drawTimer.Stop() } f.eventCh = make(chan struct{}, 30) // A large value + + if opt.query != "" { + f.state.input = []rune(opt.query) + f.state.cursorX = runewidth.StringWidth(opt.query) + f.state.x = len(opt.query) + f.filter() + } + return nil } @@ -736,6 +744,10 @@ func (f *finder) find(slice interface{}, itemFunc func(i int) string, opts []Opt close(inited) + if opt.selectOne && len(f.state.matched) == 1 { + return []int{f.state.matched[0].Idx}, nil + } + go func() { for { select { diff --git a/fuzzyfinder_test.go b/fuzzyfinder_test.go index 2bc7dd5..a5f2102 100644 --- a/fuzzyfinder_test.go +++ b/fuzzyfinder_test.go @@ -473,6 +473,113 @@ func TestFind_withContext(t *testing.T) { }) } +func TestFind_WithQuery(t *testing.T) { + t.Parallel() + var ( + things = []string{"one", "three2one"} + thingFunc = func(i int) string { + return things[i] + } + events = append(runes("one"), key(input{tcell.KeyEnter, rune(tcell.KeyEnter), tcell.ModNone})) + ) + + t.Run("no initial query", func(t *testing.T) { + f, term := fuzzyfinder.NewWithMockedTerminal() + term.SetEventsV2(events...) + + assertWithGolden(t, func(t *testing.T) string { + idx, err := f.Find(things, thingFunc) + if err != nil { + t.Fatalf("Find must not return an error, but got '%s'", err) + } + if idx != 0 { + t.Errorf("expected index: 0, but got %d", idx) + } + res := term.GetResult() + return res + }) + }) + + t.Run("has initial query", func(t *testing.T) { + f, term := fuzzyfinder.NewWithMockedTerminal() + term.SetEventsV2(events...) + + assertWithGolden(t, func(t *testing.T) string { + idx, err := f.Find(things, thingFunc, fuzzyfinder.WithQuery("three2")) + + if err != nil { + t.Fatalf("Find must not return an error, but got '%s'", err) + } + if idx != 1 { + t.Errorf("expected index: 1, but got %d", idx) + } + res := term.GetResult() + return res + }) + }) +} + +func TestFind_WithSelectOne(t *testing.T) { + t.Parallel() + + cases := map[string]struct { + things []string + moreOpts []fuzzyfinder.Option + expected int + abort bool + }{ + "only one option": { + things: []string{"one"}, + expected: 0, + }, + "more than one": { + things: []string{"one", "two"}, + abort: true, + }, + "has initial query": { + things: []string{"one", "two"}, + moreOpts: []fuzzyfinder.Option{ + fuzzyfinder.WithQuery("two"), + }, + expected: 1, + }, + } + + for name, c := range cases { + c := c + + t.Run(name, func(t *testing.T) { + t.Parallel() + f, term := fuzzyfinder.NewWithMockedTerminal() + term.SetEventsV2(key(input{tcell.KeyEsc, rune(tcell.KeyEsc), tcell.ModNone})) + + assertWithGolden(t, func(t *testing.T) string { + idx, err := f.Find( + c.things, + func(i int) string { + return c.things[i] + }, + append(c.moreOpts, fuzzyfinder.WithSelectOne())..., + ) + if c.abort { + if !errors.Is(err, fuzzyfinder.ErrAbort) { + t.Fatalf("Find must return ErrAbort, but got '%s'", err) + } + } else { + if err != nil { + t.Fatalf("Find must not return an error, but got '%s'", err) + } + if idx != c.expected { + t.Errorf("expected index: %d, but got %d", c.expected, idx) + } + } + res := term.GetResult() + return res + }) + }) + } +} + func TestFind_error(t *testing.T) { t.Parallel() diff --git a/option.go b/option.go index 80ccf52..271989e 100644 --- a/option.go +++ b/option.go @@ -15,6 +15,8 @@ type opt struct { header string beginAtTop bool context context.Context + query string + selectOne bool } type mode int @@ -127,3 +129,17 @@ func WithContext(ctx context.Context) Option { o.context = ctx } } + +// WithQuery enables to set the initial query. +func WithQuery(s string) Option { + return func(o *opt) { + o.query = s + } +} + +// WithQuery enables to set the initial query. +func WithSelectOne() Option { + return func(o *opt) { + o.selectOne = true + } +} diff --git a/testdata/fixtures/testfind_withquery-has_initial_query.golden b/testdata/fixtures/testfind_withquery-has_initial_query.golden new file mode 100644 index 0000000..6b31188 --- /dev/null +++ b/testdata/fixtures/testfind_withquery-has_initial_query.golden @@ -0,0 +1,11 @@ + + + + + + + +> three2one + 1/2 +> three2one█ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withquery-no_initial_query.golden b/testdata/fixtures/testfind_withquery-no_initial_query.golden new file mode 100644 index 0000000..9eaeb40 --- /dev/null +++ b/testdata/fixtures/testfind_withquery-no_initial_query.golden @@ -0,0 +1,11 @@ + + + + + + + three2one +> one + 2/2 +> one█ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withselectone-has_initial_query.golden b/testdata/fixtures/testfind_withselectone-has_initial_query.golden new file mode 100644 index 0000000..f10422b --- /dev/null +++ b/testdata/fixtures/testfind_withselectone-has_initial_query.golden @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withselectone-more_than_one.golden b/testdata/fixtures/testfind_withselectone-more_than_one.golden new file mode 100644 index 0000000..5cc5239 --- /dev/null +++ b/testdata/fixtures/testfind_withselectone-more_than_one.golden @@ -0,0 +1,11 @@ + + + + + + + two +> one + 2/2 +> █ + \ No newline at end of file diff --git a/testdata/fixtures/testfind_withselectone-only_one_option.golden b/testdata/fixtures/testfind_withselectone-only_one_option.golden new file mode 100644 index 0000000..f10422b --- /dev/null +++ b/testdata/fixtures/testfind_withselectone-only_one_option.golden @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file