on main help sign in
fix: improve group filtering (#1024)
861db84Carlos Alexandro Becker
internal/tui/exp/list/filterable_group.go+27−85
@@ -180,11 +180,6 @@ func (f *filterableGroupList[T]) inputHeight() int {
180 return lipgloss.Height(f.inputStyle.Render(f.input.View()))
180 return lipgloss.Height(f.inputStyle.Render(f.input.View()))
181}
181}
182
182
183−type groupMatch[T FilterableItem] struct {
184− group Group[T]
185− score int
186−}
187−
188func (f *filterableGroupList[T]) clearItemState() []tea.Cmd {
183func (f *filterableGroupList[T]) clearItemState() []tea.Cmd {
189 var cmds []tea.Cmd
184 var cmds []tea.Cmd
190 for _, item := range slices.Collect(f.items.Seq()) {
185 for _, item := range slices.Collect(f.items.Seq()) {
@@ -211,25 +206,6 @@ func (f *filterableGroupList[T]) setMatchIndexes(item T, indexes []int) {
211 }
206 }
212}
207}
213
208
214−func (f *filterableGroupList[T]) findMatchingGroups(firstWord string) []groupMatch[T] {
215− var matchedGroups []groupMatch[T]
216− for _, g := range f.groups {
217− groupName := f.getGroupName(g)
218− matches := fuzzy.Find(firstWord, []string{groupName})
219− if len(matches) > 0 && matches[0].Score > 0 {
220− matchedGroups = append(matchedGroups, groupMatch[T]{
221− group: g,
222− score: matches[0].Score,
223− })
224− }
225− }
226− // Sort by score (higher scores first - exact matches will have higher scores)
227− sort.SliceStable(matchedGroups, func(i, j int) bool {
228− return matchedGroups[i].score > matchedGroups[j].score
229− })
230− return matchedGroups
231−}
232−
233func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string) []T {
209func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string) []T {
234 if query == "" {
210 if query == "" {
235 // No query, return all items with cleared match indexes
211 // No query, return all items with cleared match indexes
@@ -241,24 +217,31 @@ func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string
241 return items
217 return items
242 }
218 }
243
219
244− // Build search words
220+ name := f.getGroupName(group) + " "
221+
245− words := make([]string, len(group.Items))
222+ names := make([]string, len(group.Items))
246 for i, item := range group.Items {
223 for i, item := range group.Items {
247− words[i] = strings.ToLower(item.FilterValue())
224+ names[i] = strings.ToLower(name + item.FilterValue())
248 }
225 }
249
226
250− // Perform fuzzy search
251− matches := fuzzy.Find(query, words)
227+ matches := fuzzy.Find(query, names)
252 sort.SliceStable(matches, func(i, j int) bool {
228 sort.SliceStable(matches, func(i, j int) bool {
253 return matches[i].Score > matches[j].Score
229 return matches[i].Score > matches[j].Score
254 })
230 })
255
231
256 if len(matches) > 0 {
232 if len(matches) > 0 {
257− // Found matches, return only those with highlights
258 var matchedItems []T
233 var matchedItems []T
259 for _, match := range matches {
234 for _, match := range matches {
260 item := group.Items[match.Index]
235 item := group.Items[match.Index]
236+ var idxs []int
237+ for _, idx := range match.MatchedIndexes {
238+ // adjusts removing group name highlights
239+ if idx < len(name) {
240+ continue
241+ }
242+ idxs = append(idxs, idx-len(name))
243+ }
261− f.setMatchIndexes(item, match.MatchedIndexes)
244+ f.setMatchIndexes(item, idxs)
262 matchedItems = append(matchedItems, item)
245 matchedItems = append(matchedItems, item)
263 }
246 }
264 return matchedItems
247 return matchedItems
@@ -267,20 +250,6 @@ func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string
267 return []T{}
250 return []T{}
268}
251}
269
252
270−func (f *filterableGroupList[T]) searchAllGroups(query string) []Group[T] {
271− var newGroups []Group[T]
272− for _, g := range f.groups {
273− matchedItems := f.filterItemsInGroup(g, query)
274− if len(matchedItems) > 0 {
275− newGroups = append(newGroups, Group[T]{
276− Section: g.Section,
277− Items: matchedItems,
278− })
279− }
280− }
281− return newGroups
282−}
283−
284func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
253func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
285 cmds := f.clearItemState()
254 cmds := f.clearItemState()
286 f.selectedItem = ""
255 f.selectedItem = ""
@@ -289,51 +258,24 @@ func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
289 return f.groupedList.SetGroups(f.groups)
258 return f.groupedList.SetGroups(f.groups)
290 }
259 }
291
260
292− lowerQuery := strings.ToLower(query)
261+ query = strings.ToLower(strings.ReplaceAll(query, " ", ""))
293− queryWords := strings.Fields(lowerQuery)
294− firstWord := queryWords[0]
295
262
296− // Find groups that match the first word
297− matchedGroups := f.findMatchingGroups(firstWord)
298−
299− var newGroups []Group[T]
263+ var result []Group[T]
300− if len(matchedGroups) > 0 {
264+ for _, g := range f.groups {
301− // Filter within matched groups using remaining words
265+ if matches := fuzzy.Find(query, []string{f.getGroupName(g)}); len(matches) > 0 && matches[0].Score > 0 {
302− remainingQuery := ""
266+ result = append(result, g)
303− if len(queryWords) > 1 {
267+ continue
304− remainingQuery = strings.Join(queryWords[1:], " ")
305− }
306−
307− for _, matchedGroup := range matchedGroups {
308− matchedItems := f.filterItemsInGroup(matchedGroup.group, remainingQuery)
309− if len(matchedItems) > 0 {
310− newGroups = append(newGroups, Group[T]{
311− Section: matchedGroup.group.Section,
312− Items: matchedItems,
313− })
314− }
315 }
268 }
316−
269+ matchedItems := f.filterItemsInGroup(g, query)
317− // add any matching items from other groups
270+ if len(matchedItems) > 0 {
318− allGroups := f.searchAllGroups(lowerQuery)
271+ result = append(result, Group[T]{
319− for _, g := range allGroups {
272+ Section: g.Section,
320− exists := false
273+ Items: matchedItems,
321− for _, existing := range newGroups {
322− if existing.Section.ID() == g.Section.ID() {
323− exists = true
324− break
325− }
326− }
327− if !exists {
328− newGroups = append(newGroups, g)
329− }
274+ })
330 }
275 }
331− } else {
332− // No group matches, search all groups
333− newGroups = f.searchAllGroups(lowerQuery)
334 }
276 }
335
277
336− cmds = append(cmds, f.groupedList.SetGroups(newGroups))
278+ cmds = append(cmds, f.groupedList.SetGroups(result))
337 return tea.Batch(cmds...)
279 return tea.Batch(cmds...)
338}
280}
339
281