diff --git a/filter/command.go b/filter/command.go index 72deafc..abf8555 100644 --- a/filter/command.go +++ b/filter/command.go @@ -79,6 +79,7 @@ func (o Options) Run() error { selected: make(map[string]struct{}), limit: o.Limit, reverse: o.Reverse, + exact: o.Exact, }, options...) tm, err := p.StartReturningModel() diff --git a/filter/filter.go b/filter/filter.go index 41c14e7..f94abee 100644 --- a/filter/filter.go +++ b/filter/filter.go @@ -42,6 +42,7 @@ type model struct { selectedPrefixStyle lipgloss.Style unselectedPrefixStyle lipgloss.Style reverse bool + exact bool } func (m model) Init() tea.Cmd { return nil } @@ -180,6 +181,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { // https://github.com/sahilm/fuzzy m.matches = fuzzy.Find(m.textinput.Value(), m.choices) + // For exact search return only the exact matches + if m.exact { + m.matches = exactMatches(m.textinput.Value(), m.matches) + } + // If the search field is empty, let's not display the matches // (none), but rather display all possible choices. if m.textinput.Value() == "" { @@ -247,6 +253,30 @@ func matchAll(options []string) []fuzzy.Match { return matches } +func exactMatches(search string, matches []fuzzy.Match) []fuzzy.Match { + if len(search) < 2 { + return matches + } + + exactMatches := fuzzy.Matches{} + for _, m := range matches { + search = strings.ToLower(search) + matchedString := strings.ToLower(m.Str) + + index := strings.Index(matchedString, search) + if index >= 0 { + // we need to override the MatchedIndexes because sometimes + // they are not consecutive, but they have to be in the exact search + m.MatchedIndexes = []int{} + for i := range search { + m.MatchedIndexes = append(m.MatchedIndexes, index+i) + } + exactMatches = append(exactMatches, m) + } + } + return exactMatches +} + //nolint:unparam func clamp(min, max, val int) int { if val < min { diff --git a/filter/options.go b/filter/options.go index e489390..889c3f3 100644 --- a/filter/options.go +++ b/filter/options.go @@ -21,4 +21,5 @@ type Options struct { Height int `help:"Input height" default:"0" env:"GUM_FILTER_HEIGHT"` Value string `help:"Initial filter value" default:"" env:"GUM_FILTER_VALUE"` Reverse bool `help:"Display from the bottom of the screen" env:"GUM_FILTER_REVERSE"` + Exact bool `help:"Enable exact-match" env:"GUM_FILTER_EXACT"` }