Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add SetFilterText and SetFilterState #335

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 36 additions & 7 deletions list/list.go
Expand Up @@ -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.
Expand Down Expand Up @@ -87,7 +88,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 {
Expand Down Expand Up @@ -255,12 +256,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 := filterItems(*m)
taigrr marked this conversation as resolved.
Show resolved Hide resolved
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 = state
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.
Expand Down
65 changes: 64 additions & 1 deletion list/list_test.go
Expand Up @@ -3,6 +3,7 @@ package list
import (
"fmt"
"io"
"reflect"
"strings"
"testing"

Expand All @@ -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{}

Expand Down Expand Up @@ -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)
}
}