Skip to content

Conversation

@elifarley
Copy link

@elifarley elifarley commented Nov 7, 2025

This PR introduces the --output-indexes flag to gum filter. When set, gum filter will output the 0-based index (or indexes) of the selected items instead of their string values.

Motivation

When gum filter is used in scripts, it's often much easier and more robust to work with the selected indexes rather than the selected values.

The problem is most evident when the list provided to gum filter contains visually rich formatting. For example, a script might display a list of git branches, formatting each line with ANSI colors, commit hashes, and other details:

feature-branch  (3a4f8c) 🎨 Add new styles
main            (9b1d0e) 🚀 Merge PR #123
bugfix/login    (c5e2a9) 🐛 Fix auth issue

If the user selects a line, gum filter currently returns the entire, complex string, including all the formatting and ANSI control characters: bugfix/login \x1b[33m(c5e2a9)\x1b[0m 🐛 Fix auth issue (in fact, it strips a few of those control characters, making the match fail)

For the calling script, trying to parse this string to get the original branch name (bugfix/login) is difficult and error-prone.

This PR provides a simple, robust solution. By using --output-indexes, the script receives a simple integer (e.g., 2), which it can use to directly look up the item from its original, unformatted list.

(I created this PR so that I will be able to simplify function gum_filter_by_index in my Git / Mercurial client)

Changes

  • Added --output-indexes flag to return 0-based indexes instead of values.
  • Optimized multiple selections using an index map for better performance (O(n+m) vs O(n*m)).
  • Added defensive validation to skip invalid indexes.
  • Handles duplicate choices by finding all matching indexes.

Usage

Default behavior (returns value):

$ echo -e "apple\nbanana\ncherry" | gum filter --value="banana" --select-if-one
# Output: banana

New behavior (returns index):

$ echo -e "apple\nbanana\ncherry" | gum filter --value="banana" --select-if-one --output-indexes
# Output: 1

Environment variable support:

export GUM_FILTER_OUTPUT_INDEXES=true
echo -e "apple\nbanana\ncherry" | gum filter --value="cherry" --select-if-one
# Output: 2

Copilot AI and others added 7 commits November 7, 2025 16:10
Co-authored-by: elifarley <519940+elifarley@users.noreply.github.com>
…r performance

Co-authored-by: elifarley <519940+elifarley@users.noreply.github.com>
Co-authored-by: elifarley <519940+elifarley@users.noreply.github.com>
Co-authored-by: elifarley <519940+elifarley@users.noreply.github.com>
Co-authored-by: elifarley <519940+elifarley@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings November 7, 2025 18:57
@elifarley elifarley requested a review from a team as a code owner November 7, 2025 18:57
@elifarley elifarley requested review from raphamorim and tauraamui and removed request for a team November 7, 2025 18:57
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds a new OutputIndexes option to the filter command that allows outputting 0-based indexes of selected items instead of their values.

  • Adds OutputIndexes boolean flag to Options struct
  • Implements findIndex helper method with corresponding unit tests
  • Updates output logic in Run() and checkSelected() to conditionally output indexes when flag is enabled

Reviewed Changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
filter/options.go Adds new OutputIndexes boolean field to enable index-based output
filter/filter_test.go Adds unit tests for the findIndex helper method
filter/command.go Implements index output logic in single/multiple selection paths and adds findIndex helper

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +188 to +191
for i, choice := range filteringChoices {
if choice == k {
indexes = append(indexes, strconv.Itoa(i))
}
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested loop in checkSelected has O(n*m) complexity where n is the number of selected items and m is the total number of choices. For multiple selections with many choices, this could be inefficient. Consider using the existing findIndex helper method which provides the same functionality but with clearer intent: idx := o.findIndex(k, filteringChoices); if idx >= 0 { indexes = append(indexes, strconv.Itoa(idx)) }

Suggested change
for i, choice := range filteringChoices {
if choice == k {
indexes = append(indexes, strconv.Itoa(i))
}
idx := o.findIndex(k, filteringChoices)
if idx >= 0 {
indexes = append(indexes, strconv.Itoa(idx))

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +193
// For each selected item, find all indexes in filteringChoices (handles duplicates)
indexes := make([]string, 0, len(m.selected))
for k := range m.selected {
for i, choice := range filteringChoices {
if choice == k {
indexes = append(indexes, strconv.Itoa(i))
}
}
}
Copy link

Copilot AI Nov 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment states this 'handles duplicates', but the implementation will output multiple indexes for duplicate values in filteringChoices. Since m.selected is a map with unique keys, if the same value appears multiple times in the original choices list, all matching indexes will be output. This behavior may be unexpected - if a user selects one item that appears at positions 2 and 5, both indexes will be returned. The single-selection path at lines 89-92 and 171-174 uses findIndex which only returns the first occurrence. Consider documenting this behavior difference or making it consistent.

Suggested change
// For each selected item, find all indexes in filteringChoices (handles duplicates)
indexes := make([]string, 0, len(m.selected))
for k := range m.selected {
for i, choice := range filteringChoices {
if choice == k {
indexes = append(indexes, strconv.Itoa(i))
}
}
}
// For each selected item, find the first index in filteringChoices (consistent with single-selection)
indexes := make([]string, 0, len(m.selected))
for k := range m.selected {
idx := o.findIndex(k, filteringChoices)
if idx >= 0 {
indexes = append(indexes, strconv.Itoa(idx))
}
}

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant