gum/choose/command.go
Yachen Mao 9a3e11f542
Add key/value feature for gum choose (#530) (#598)
* Add key/value feature for `gum choose` (#530)

* trim the space of key and value

* using SplitN
2024-07-12 00:26:54 -04:00

150 lines
3.1 KiB
Go

package choose
import (
"errors"
"fmt"
"os"
"strings"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
"github.com/mattn/go-isatty"
"github.com/charmbracelet/gum/ansi"
"github.com/charmbracelet/gum/internal/stdin"
)
const widthBuffer = 2
// Run provides a shell script interface for choosing between different through
// options.
func (o Options) Run() error {
if len(o.Options) <= 0 {
input, _ := stdin.Read()
if input == "" {
return errors.New("no options provided, see `gum choose --help`")
}
o.Options = strings.Split(input, "\n")
}
if o.SelectIfOne && len(o.Options) == 1 {
fmt.Println(o.Options[0])
return nil
}
theme := huh.ThemeCharm()
options := make([]huh.Option[string], len(o.Options))
for i, option := range o.Options {
parsed := strings.SplitN(option, o.Deliminator, 2)
if len(parsed) == 2 {
key := strings.TrimSpace(parsed[0])
value := strings.TrimSpace(parsed[1])
options[i] = huh.NewOption(key, value)
} else {
options[i] = huh.NewOption(option, option)
}
}
theme.Focused.Base = lipgloss.NewStyle()
theme.Focused.Title = o.HeaderStyle.ToLipgloss()
theme.Focused.SelectSelector = o.CursorStyle.ToLipgloss().SetString(o.Cursor)
theme.Focused.MultiSelectSelector = o.CursorStyle.ToLipgloss().SetString(o.Cursor)
theme.Focused.SelectedOption = o.SelectedItemStyle.ToLipgloss()
theme.Focused.UnselectedOption = o.ItemStyle.ToLipgloss()
theme.Focused.SelectedPrefix = o.SelectedItemStyle.ToLipgloss().SetString(o.SelectedPrefix)
theme.Focused.UnselectedPrefix = o.ItemStyle.ToLipgloss().SetString(o.UnselectedPrefix)
for _, s := range o.Selected {
for i, opt := range options {
if s == opt.Key || s == opt.Value {
options[i] = opt.Selected(true)
}
}
}
if o.NoLimit {
o.Limit = len(o.Options)
}
width := max(widest(o.Options)+
max(lipgloss.Width(o.SelectedPrefix)+lipgloss.Width(o.UnselectedPrefix))+
lipgloss.Width(o.Cursor)+1, lipgloss.Width(o.Header)+widthBuffer)
if o.Limit > 1 {
var choices []string
field := huh.NewMultiSelect[string]().
Options(options...).
Title(o.Header).
Height(o.Height).
Limit(o.Limit).
Value(&choices)
form := huh.NewForm(huh.NewGroup(field))
err := form.
WithWidth(width).
WithShowHelp(o.ShowHelp).
WithTheme(theme).
WithTimeout(o.Timeout).
Run()
if err != nil {
return err
}
if len(choices) > 0 {
s := strings.Join(choices, "\n")
ansiprint(s)
}
return nil
}
var choice string
err := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Options(options...).
Title(o.Header).
Height(o.Height).
Value(&choice),
),
).
WithWidth(width).
WithTheme(theme).
WithShowHelp(o.ShowHelp).
WithTimeout(o.Timeout).
Run()
if err != nil {
return err
}
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println(choice)
} else {
fmt.Print(ansi.Strip(choice))
}
return nil
}
func widest(options []string) int {
var max int
for _, o := range options {
w := lipgloss.Width(o)
if w > max {
max = w
}
}
return max
}
func ansiprint(s string) {
if isatty.IsTerminal(os.Stdout.Fd()) {
fmt.Println(s)
} else {
fmt.Print(ansi.Strip(s))
}
}