Liza Shulyayeva

Implementing multiple choice selection in Go with promptui



This is just a short post about how I chose to implement multiple choice selection in Go with the promptui CLI library.

promptui doesn’t natively support multiple choice selection, so I’m using an instance of promptui.Select to toggle selection status and re-run another selection prompt when an item is chosen via the CLI. When a “Done” item is selected, all the selected items are returned. You can see the code on GitHub, and below (see inline comments):

type item struct {
    ID         string
    IsSelected bool
}

// selectItems() prompts user to select one or more items in the given slice
func selectItems(selectedPos int, allItems []*item) ([]*item, error) {
    // Always prepend a "Done" item to the slice if it doesn't
    // already exist.
    const doneID = "Done"
    if len(allItems) > 0 && allItems[0].ID != doneID {
        var items = []*item{
            {
                ID: doneID,
            },
        }
        allItems = append(items, allItems...)
    }
    
    // Define promptui template
    templates := &promptui.SelectTemplates{
        Label: `{{if .IsSelected}}
                {{end}} {{ .ID }} - label`,
        Active:   "→ {{if .IsSelected}}✔ {{end}}{{ .ID | cyan }}",
        Inactive: "{{if .IsSelected}}✔ {{end}}{{ .ID | cyan }}",
    }
    
    prompt := promptui.Select{
        Label:     "Item",
        Items:     allItems,
        Templates: templates,
        Size:      5,
        // Start the cursor at the currently selected index
        CursorPos:    selectedPos,
        HideSelected: true,
    }
    
    selectionIdx, _, err := prompt.Run()
        if err != nil {
        return nil, fmt.Errorf("prompt failed: %w", err)
    }
    
    chosenItem := allItems[selectionIdx]
    
    if chosenItem.ID != doneID {
        // If the user selected something other than "Done",
        // toggle selection on this item and run the function again.
        chosenItem.IsSelected = !chosenItem.IsSelected
        return selectItems(selectionIdx, allItems)
    }
    
    // If the user selected the "Done" item, return
    // all selected items.
    var selectedItems []*item
    for _, i := range allItems {
        if i.IsSelected {
            selectedItems = append(selectedItems, i)
        }
    }
    return selectedItems, nil
}

The result looks like this:

Multiple selection via CLI in Go

© 2023 · Liza Shulyayeva · Top · RSS · privacy policy