summaryrefslogtreecommitdiffstats
path: root/pkg/gui/context/filtered_list.go
blob: 0724ecb3b8d8e6648c7e42dcf67c8fa6e9a607c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package context

import (
	"strings"

	"github.com/jesseduffield/lazygit/pkg/utils"
	"github.com/sahilm/fuzzy"
	"github.com/samber/lo"
	"github.com/sasha-s/go-deadlock"
)

type FilteredList[T any] struct {
	filteredIndices []int // if nil, we are not filtering

	getList         func() []T
	getFilterFields func(T) []string
	filter          string

	mutex *deadlock.Mutex
}

func NewFilteredList[T any](getList func() []T, getFilterFields func(T) []string) *FilteredList[T] {
	return &FilteredList[T]{
		getList:         getList,
		getFilterFields: getFilterFields,
		mutex:           &deadlock.Mutex{},
	}
}

func (self *FilteredList[T]) GetFilter() string {
	return self.filter
}

func (self *FilteredList[T]) SetFilter(filter string, useFuzzySearch bool) {
	self.filter = filter

	self.applyFilter(useFuzzySearch)
}

func (self *FilteredList[T]) ClearFilter() {
	self.SetFilter("", false)
}

func (self *FilteredList[T]) ReApplyFilter(useFuzzySearch bool) {
	self.applyFilter(useFuzzySearch)
}

func (self *FilteredList[T]) IsFiltering() bool {
	return self.filter != ""
}

func (self *FilteredList[T]) GetFilteredList() []T {
	if self.filteredIndices == nil {
		return self.getList()
	}
	return utils.ValuesAtIndices(self.getList(), self.filteredIndices)
}

// TODO: update to just 'Len'
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(useFuzzySearch bool) {
	self.mutex.Lock()
	defer self.mutex.Unlock()

	if self.filter == "" {
		self.filteredIndices = nil
	} else {
		source := &fuzzySource[T]{
			list:            self.getList(),
			getFilterFields: self.getFilterFields,
		}

		matches := utils.FindFrom(self.filter, source, useFuzzySearch)
		self.filteredIndices = lo.Map(matches, func(match fuzzy.Match, _ int) int {
			return match.Index
		})
	}
}

func (self *FilteredList[T]) UnfilteredIndex(index int) int {
	self.mutex.Lock()
	defer self.mutex.Unlock()

	if self.filteredIndices == nil {
		return index
	}

	// we use -1 when there are no items
	if index == -1 {
		return -1
	}

	return self.filteredIndices[index]
}