From d22ff9adf0f3a647edfc9fffb845e1abf1616d8e Mon Sep 17 00:00:00 2001 From: Jesse Duffield Date: Sat, 22 Jul 2023 13:04:39 +1000 Subject: [PATCH] Use fuzzy search when filtering a view This adds fuzzy filtering instead of exact match filtering, which is more forgiving of typos and allows more efficiency. --- pkg/gui/context/filtered_list.go | 39 ++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/pkg/gui/context/filtered_list.go b/pkg/gui/context/filtered_list.go index 298d3d615d0..d2361ad53f9 100644 --- a/pkg/gui/context/filtered_list.go +++ b/pkg/gui/context/filtered_list.go @@ -1,7 +1,11 @@ package context import ( + "strings" + "github.com/jesseduffield/lazygit/pkg/utils" + "github.com/sahilm/fuzzy" + "github.com/samber/lo" "github.com/sasha-s/go-deadlock" ) @@ -53,6 +57,21 @@ func (self *FilteredList[T]) UnfilteredLen() int { return len(self.getList()) } +type fuzzySource[T any] struct { + list []T + getFilterFields func(T) []string +} + +var _ fuzzy.Source = &fuzzySource[string]{} + +func (self *fuzzySource[T]) String(i int) string { + return strings.Join(self.getFilterFields(self.list[i]), " ") +} + +func (self *fuzzySource[T]) Len() int { + return len(self.list) +} + func (self *FilteredList[T]) applyFilter() { self.mutex.Lock() defer self.mutex.Unlock() @@ -60,20 +79,16 @@ func (self *FilteredList[T]) applyFilter() { if self.filter == "" { self.filteredIndices = nil } else { - self.filteredIndices = []int{} - for i, item := range self.getList() { - for _, field := range self.getFilterFields(item) { - if self.match(field, self.filter) { - self.filteredIndices = append(self.filteredIndices, i) - break - } - } + source := &fuzzySource[T]{ + list: self.getList(), + getFilterFields: self.getFilterFields, } - } -} -func (self *FilteredList[T]) match(haystack string, needle string) bool { - return utils.CaseAwareContains(haystack, needle) + matches := fuzzy.FindFrom(self.filter, source) + self.filteredIndices = lo.Map(matches, func(match fuzzy.Match, _ int) int { + return match.Index + }) + } } func (self *FilteredList[T]) UnfilteredIndex(index int) int {