From fde567b09871ab470d8cdc88a2d3e83956331c37 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Tue, 21 Feb 2023 01:22:25 -0800 Subject: [PATCH 1/5] Add SetFilterText and SetFilterState --- list/list.go | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/list/list.go b/list/list.go index d6bb3ea4..3cfc6d08 100644 --- a/list/list.go +++ b/list/list.go @@ -87,7 +87,7 @@ type Rank struct { // DefaultFilter uses the sahilm/fuzzy to filter through the list. // This is set by default. func DefaultFilter(term string, targets []string) []Rank { - var ranks = fuzzy.Find(term, targets) + ranks := fuzzy.Find(term, targets) sort.Stable(ranks) result := make([]Rank, len(ranks)) for i, r := range ranks { @@ -255,12 +255,40 @@ func (m *Model) SetShowTitle(v bool) { m.updatePagination() } +// SetFilterText explicitly sets the filter text without relying on user input. +// It also sets the filterState to a sane default of FilterApplied, but this +// can be changed with SetFilterState +func (m *Model) SetFilterText(filter string) { + m.filterState = Filtering + m.FilterInput.SetValue(filter) + cmd := tea.Cmd(filterItems(*m)) + msg := cmd() + fmm, _ := msg.(FilterMatchesMsg) + m.filteredItems = filteredItems(fmm) + m.filterState = FilterApplied + m.Paginator.Page = 0 + m.cursor = 0 + m.FilterInput.CursorEnd() + m.updatePagination() + m.updateKeybindings() +} + +// Helper method for setting the filtering state manually +func (m *Model) SetFilterState(state FilterState) { + m.Paginator.Page = 0 + m.cursor = 0 + m.filterState = Filtering + m.FilterInput.CursorEnd() + m.FilterInput.Focus() + m.updateKeybindings() +} + // ShowTitle returns whether or not the title bar is set to be rendered. func (m Model) ShowTitle() bool { return m.showTitle } -// SetShowFilter shows or hides the filer bar. Note that this does not disable +// SetShowFilter shows or hides the filter bar. Note that this does not disable // filtering, it simply hides the built-in filter view. This allows you to // use the FilterInput to render the filtering UI differently without having to // re-implement filtering from scratch. From dd3c23bba3634c816f4ef15ae97ce067f56e3db1 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Tue, 21 Feb 2023 15:10:20 -0800 Subject: [PATCH 2/5] make linter happy --- list/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/list/list.go b/list/list.go index 3cfc6d08..d635371e 100644 --- a/list/list.go +++ b/list/list.go @@ -261,7 +261,7 @@ func (m *Model) SetShowTitle(v bool) { func (m *Model) SetFilterText(filter string) { m.filterState = Filtering m.FilterInput.SetValue(filter) - cmd := tea.Cmd(filterItems(*m)) + cmd := filterItems(*m) msg := cmd() fmm, _ := msg.(FilterMatchesMsg) m.filteredItems = filteredItems(fmm) From 6776f8f92688f3726b412ff2fff909c33e58978c Mon Sep 17 00:00:00 2001 From: k-x7 <97758178+k-x7@users.noreply.github.com> Date: Wed, 22 Feb 2023 02:48:43 +0300 Subject: [PATCH 3/5] make SetFilterState state not hardcoded --- list/list.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/list/list.go b/list/list.go index d635371e..3cdf744c 100644 --- a/list/list.go +++ b/list/list.go @@ -277,7 +277,7 @@ func (m *Model) SetFilterText(filter string) { func (m *Model) SetFilterState(state FilterState) { m.Paginator.Page = 0 m.cursor = 0 - m.filterState = Filtering + m.filterState = state m.FilterInput.CursorEnd() m.FilterInput.Focus() m.updateKeybindings() From 1c447da837c2ae83ecdfb4693608bfac678c8d36 Mon Sep 17 00:00:00 2001 From: k-x7 <97758178+k-x7@users.noreply.github.com> Date: Wed, 22 Feb 2023 03:05:49 +0300 Subject: [PATCH 4/5] tests for SetFilterText and SetFilterState --- list/list_test.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 64 insertions(+), 1 deletion(-) diff --git a/list/list_test.go b/list/list_test.go index 2c925aab..2627e5b1 100644 --- a/list/list_test.go +++ b/list/list_test.go @@ -3,6 +3,7 @@ package list import ( "fmt" "io" + "reflect" "strings" "testing" @@ -11,7 +12,7 @@ import ( type item string -func (i item) FilterValue() string { return "" } +func (i item) FilterValue() string { return string(i) } type itemDelegate struct{} @@ -72,3 +73,65 @@ func TestCustomStatusBarItemName(t *testing.T) { t.Fatalf("Error: expected view to contain %s", expected) } } + +func TestSetFilterText(t *testing.T) { + tc := []Item{item("foo"), item("bar"), item("baz")} + + list := New(tc, itemDelegate{}, 10, 10) + list.SetFilterText("ba") + + list.SetFilterState(Unfiltered) + expected := tc + // TODO: replace with slices.Equal() when project move to go1.18 or later + if !reflect.DeepEqual(list.VisibleItems(), expected) { + t.Fatalf("Error: expected view to contain only %s", expected) + } + + list.SetFilterState(Filtering) + expected = []Item{item("bar"), item("baz")} + if !reflect.DeepEqual(list.VisibleItems(), expected) { + t.Fatalf("Error: expected view to contain only %s", expected) + } + + list.SetFilterState(FilterApplied) + if !reflect.DeepEqual(list.VisibleItems(), expected) { + t.Fatalf("Error: expected view to contain only %s", expected) + } +} + +func TestSetFilterState(t *testing.T) { + tc := []Item{item("foo"), item("bar"), item("baz")} + + list := New(tc, itemDelegate{}, 10, 10) + list.SetFilterText("ba") + + list.SetFilterState(Unfiltered) + expected, notExpected := "up", "clear filter" + + lines := strings.Split(list.View(), "\n") + footer := lines[len(lines)-1] + + if !strings.Contains(footer, expected) || strings.Contains(footer, notExpected) { + t.Fatalf("Error: expected view to contain '%s' not '%s'", expected, notExpected) + } + + list.SetFilterState(Filtering) + expected, notExpected = "filter", "more" + + lines = strings.Split(list.View(), "\n") + footer = lines[len(lines)-1] + + if !strings.Contains(footer, expected) || strings.Contains(footer, notExpected) { + t.Fatalf("Error: expected view to contain '%s' not '%s'", expected, notExpected) + } + + list.SetFilterState(FilterApplied) + expected = "clear" + + lines = strings.Split(list.View(), "\n") + footer = lines[len(lines)-1] + + if !strings.Contains(footer, expected) { + t.Fatalf("Error: expected view to contain '%s'", expected) + } +} From c800f20b615cf7fc15ad31fcec425ed5f25fe439 Mon Sep 17 00:00:00 2001 From: Tai Groot Date: Tue, 21 Feb 2023 16:12:46 -0800 Subject: [PATCH 5/5] fix small typo --- list/list.go | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/list/list.go b/list/list.go index d635371e..2db9a6b8 100644 --- a/list/list.go +++ b/list/list.go @@ -10,16 +10,17 @@ import ( "strings" "time" - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/key" - "github.com/charmbracelet/bubbles/paginator" - "github.com/charmbracelet/bubbles/spinner" - "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" "github.com/sahilm/fuzzy" + + "github.com/charmbracelet/bubbles/help" + "github.com/charmbracelet/bubbles/key" + "github.com/charmbracelet/bubbles/paginator" + "github.com/charmbracelet/bubbles/spinner" + "github.com/charmbracelet/bubbles/textinput" ) // Item is an item that appears in the list. @@ -277,7 +278,7 @@ func (m *Model) SetFilterText(filter string) { func (m *Model) SetFilterState(state FilterState) { m.Paginator.Page = 0 m.cursor = 0 - m.filterState = Filtering + m.filterState = state m.FilterInput.CursorEnd() m.FilterInput.Focus() m.updateKeybindings()