fix: improve group filtering (#1024)

Carlos Alexandro Becker created

Change summary

internal/tui/exp/list/filterable_group.go | 112 ++++++------------------
1 file changed, 27 insertions(+), 85 deletions(-)

Detailed changes

internal/tui/exp/list/filterable_group.go 🔗

@@ -180,11 +180,6 @@ func (f *filterableGroupList[T]) inputHeight() int {
 	return lipgloss.Height(f.inputStyle.Render(f.input.View()))
 }
 
-type groupMatch[T FilterableItem] struct {
-	group Group[T]
-	score int
-}
-
 func (f *filterableGroupList[T]) clearItemState() []tea.Cmd {
 	var cmds []tea.Cmd
 	for _, item := range slices.Collect(f.items.Seq()) {
@@ -211,25 +206,6 @@ func (f *filterableGroupList[T]) setMatchIndexes(item T, indexes []int) {
 	}
 }
 
-func (f *filterableGroupList[T]) findMatchingGroups(firstWord string) []groupMatch[T] {
-	var matchedGroups []groupMatch[T]
-	for _, g := range f.groups {
-		groupName := f.getGroupName(g)
-		matches := fuzzy.Find(firstWord, []string{groupName})
-		if len(matches) > 0 && matches[0].Score > 0 {
-			matchedGroups = append(matchedGroups, groupMatch[T]{
-				group: g,
-				score: matches[0].Score,
-			})
-		}
-	}
-	// Sort by score (higher scores first - exact matches will have higher scores)
-	sort.SliceStable(matchedGroups, func(i, j int) bool {
-		return matchedGroups[i].score > matchedGroups[j].score
-	})
-	return matchedGroups
-}
-
 func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string) []T {
 	if query == "" {
 		// No query, return all items with cleared match indexes
@@ -241,24 +217,31 @@ func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string
 		return items
 	}
 
-	// Build search words
-	words := make([]string, len(group.Items))
+	name := f.getGroupName(group) + " "
+
+	names := make([]string, len(group.Items))
 	for i, item := range group.Items {
-		words[i] = strings.ToLower(item.FilterValue())
+		names[i] = strings.ToLower(name + item.FilterValue())
 	}
 
-	// Perform fuzzy search
-	matches := fuzzy.Find(query, words)
+	matches := fuzzy.Find(query, names)
 	sort.SliceStable(matches, func(i, j int) bool {
 		return matches[i].Score > matches[j].Score
 	})
 
 	if len(matches) > 0 {
-		// Found matches, return only those with highlights
 		var matchedItems []T
 		for _, match := range matches {
 			item := group.Items[match.Index]
-			f.setMatchIndexes(item, match.MatchedIndexes)
+			var idxs []int
+			for _, idx := range match.MatchedIndexes {
+				// adjusts removing group name highlights
+				if idx < len(name) {
+					continue
+				}
+				idxs = append(idxs, idx-len(name))
+			}
+			f.setMatchIndexes(item, idxs)
 			matchedItems = append(matchedItems, item)
 		}
 		return matchedItems
@@ -267,20 +250,6 @@ func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string
 	return []T{}
 }
 
-func (f *filterableGroupList[T]) searchAllGroups(query string) []Group[T] {
-	var newGroups []Group[T]
-	for _, g := range f.groups {
-		matchedItems := f.filterItemsInGroup(g, query)
-		if len(matchedItems) > 0 {
-			newGroups = append(newGroups, Group[T]{
-				Section: g.Section,
-				Items:   matchedItems,
-			})
-		}
-	}
-	return newGroups
-}
-
 func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
 	cmds := f.clearItemState()
 	f.selectedItem = ""
@@ -289,51 +258,24 @@ func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
 		return f.groupedList.SetGroups(f.groups)
 	}
 
-	lowerQuery := strings.ToLower(query)
-	queryWords := strings.Fields(lowerQuery)
-	firstWord := queryWords[0]
+	query = strings.ToLower(strings.ReplaceAll(query, " ", ""))
 
-	// Find groups that match the first word
-	matchedGroups := f.findMatchingGroups(firstWord)
-
-	var newGroups []Group[T]
-	if len(matchedGroups) > 0 {
-		// Filter within matched groups using remaining words
-		remainingQuery := ""
-		if len(queryWords) > 1 {
-			remainingQuery = strings.Join(queryWords[1:], " ")
-		}
-
-		for _, matchedGroup := range matchedGroups {
-			matchedItems := f.filterItemsInGroup(matchedGroup.group, remainingQuery)
-			if len(matchedItems) > 0 {
-				newGroups = append(newGroups, Group[T]{
-					Section: matchedGroup.group.Section,
-					Items:   matchedItems,
-				})
-			}
+	var result []Group[T]
+	for _, g := range f.groups {
+		if matches := fuzzy.Find(query, []string{f.getGroupName(g)}); len(matches) > 0 && matches[0].Score > 0 {
+			result = append(result, g)
+			continue
 		}
-
-		// add any matching items from other groups
-		allGroups := f.searchAllGroups(lowerQuery)
-		for _, g := range allGroups {
-			exists := false
-			for _, existing := range newGroups {
-				if existing.Section.ID() == g.Section.ID() {
-					exists = true
-					break
-				}
-			}
-			if !exists {
-				newGroups = append(newGroups, g)
-			}
+		matchedItems := f.filterItemsInGroup(g, query)
+		if len(matchedItems) > 0 {
+			result = append(result, Group[T]{
+				Section: g.Section,
+				Items:   matchedItems,
+			})
 		}
-	} else {
-		// No group matches, search all groups
-		newGroups = f.searchAllGroups(lowerQuery)
 	}
 
-	cmds = append(cmds, f.groupedList.SetGroups(newGroups))
+	cmds = append(cmds, f.groupedList.SetGroups(result))
 	return tea.Batch(cmds...)
 }