diff --git a/choose/choose.go b/choose/choose.go deleted file mode 100644 index cc72ec8..0000000 --- a/choose/choose.go +++ /dev/null @@ -1,220 +0,0 @@ -// Package choose provides an interface to choose one option from a given list -// of options. The options can be provided as (new-line separated) stdin or a -// list of arguments. -// -// It is different from the filter command as it does not provide a fuzzy -// finding input, so it is best used for smaller lists of options. -// -// Let's pick from a list of gum flavors: -// -// $ gum choose "Strawberry" "Banana" "Cherry" -package choose - -import ( - "strings" - "time" - - "github.com/charmbracelet/gum/timeout" - - "github.com/charmbracelet/bubbles/paginator" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -type model struct { - height int - cursor string - selectedPrefix string - unselectedPrefix string - cursorPrefix string - header string - items []item - quitting bool - index int - limit int - numSelected int - currentOrder int - paginator paginator.Model - aborted bool - - // styles - cursorStyle lipgloss.Style - headerStyle lipgloss.Style - itemStyle lipgloss.Style - selectedItemStyle lipgloss.Style - hasTimeout bool - timeout time.Duration -} - -type item struct { - text string - selected bool - order int -} - -func (m model) Init() tea.Cmd { - return timeout.Init(m.timeout, nil) -} - -func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - return m, nil - case timeout.TickTimeoutMsg: - if msg.TimeoutValue <= 0 { - m.quitting = true - // If the user hasn't selected any items in a multi-select. - // Then we select the item that they have pressed enter on. If they - // have selected items, then we simply return them. - if m.numSelected < 1 { - m.items[m.index].selected = true - } - return m, tea.Quit - } - m.timeout = msg.TimeoutValue - return m, timeout.Tick(msg.TimeoutValue, msg.Data) - case tea.KeyMsg: - start, end := m.paginator.GetSliceBounds(len(m.items)) - switch keypress := msg.String(); keypress { - case "down", "j", "ctrl+j", "ctrl+n": - m.index++ - if m.index >= len(m.items) { - m.index = 0 - m.paginator.Page = 0 - } - if m.index >= end { - m.paginator.NextPage() - } - case "up", "k", "ctrl+k", "ctrl+p": - m.index-- - if m.index < 0 { - m.index = len(m.items) - 1 - m.paginator.Page = m.paginator.TotalPages - 1 - } - if m.index < start { - m.paginator.PrevPage() - } - case "right", "l", "ctrl+f": - m.index = clamp(m.index+m.height, 0, len(m.items)-1) - m.paginator.NextPage() - case "left", "h", "ctrl+b": - m.index = clamp(m.index-m.height, 0, len(m.items)-1) - m.paginator.PrevPage() - case "G", "end": - m.index = len(m.items) - 1 - m.paginator.Page = m.paginator.TotalPages - 1 - case "g", "home": - m.index = 0 - m.paginator.Page = 0 - case "a": - if m.limit <= 1 { - break - } - for i := range m.items { - if m.numSelected >= m.limit { - break // do not exceed given limit - } - if m.items[i].selected { - continue - } - m.items[i].selected = true - m.items[i].order = m.currentOrder - m.numSelected++ - m.currentOrder++ - } - case "A": - if m.limit <= 1 { - break - } - for i := range m.items { - m.items[i].selected = false - m.items[i].order = 0 - } - m.numSelected = 0 - m.currentOrder = 0 - case "ctrl+c", "esc": - m.aborted = true - m.quitting = true - return m, tea.Quit - case " ", "tab", "x", "ctrl+@": - if m.limit == 1 { - break // no op - } - - if m.items[m.index].selected { - m.items[m.index].selected = false - m.numSelected-- - } else if m.numSelected < m.limit { - m.items[m.index].selected = true - m.items[m.index].order = m.currentOrder - m.numSelected++ - m.currentOrder++ - } - case "enter": - m.quitting = true - if m.limit <= 1 && m.numSelected < 1 { - m.items[m.index].selected = true - } - return m, tea.Quit - } - } - - var cmd tea.Cmd - m.paginator, cmd = m.paginator.Update(msg) - return m, cmd -} - -func (m model) View() string { - if m.quitting { - return "" - } - - var s strings.Builder - var timeoutStr string - - start, end := m.paginator.GetSliceBounds(len(m.items)) - for i, item := range m.items[start:end] { - if i == m.index%m.height { - s.WriteString(m.cursorStyle.Render(m.cursor)) - } else { - s.WriteString(strings.Repeat(" ", lipgloss.Width(m.cursor))) - } - - if item.selected { - if m.hasTimeout { - timeoutStr = timeout.Str(m.timeout) - } - s.WriteString(m.selectedItemStyle.Render(m.selectedPrefix + item.text + timeoutStr)) - } else if i == m.index%m.height { - s.WriteString(m.cursorStyle.Render(m.cursorPrefix + item.text)) - } else { - s.WriteString(m.itemStyle.Render(m.unselectedPrefix + item.text)) - } - if i != m.height { - s.WriteRune('\n') - } - } - - if m.paginator.TotalPages > 1 { - s.WriteString(strings.Repeat("\n", m.height-m.paginator.ItemsOnPage(len(m.items))+1)) - s.WriteString(" " + m.paginator.View()) - } - - if m.header != "" { - header := m.headerStyle.Render(m.header) - return lipgloss.JoinVertical(lipgloss.Left, header, s.String()) - } - - return s.String() -} - -//nolint:unparam -func clamp(x, min, max int) int { - if x < min { - return min - } - if x > max { - return max - } - return x -} diff --git a/choose/command.go b/choose/command.go index 66691d8..466ae4e 100644 --- a/choose/command.go +++ b/choose/command.go @@ -3,29 +3,17 @@ package choose import ( "errors" "fmt" - "os" - "sort" "strings" - "github.com/charmbracelet/bubbles/paginator" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - "github.com/mattn/go-isatty" - - "github.com/charmbracelet/gum/ansi" - "github.com/charmbracelet/gum/internal/exit" "github.com/charmbracelet/gum/internal/stdin" -) - -var ( - subduedStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#847A85", Dark: "#979797"}) - verySubduedStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"}) + "github.com/charmbracelet/huh" + "github.com/charmbracelet/lipgloss" ) // Run provides a shell script interface for choosing between different through // options. func (o Options) Run() error { - if len(o.Options) == 0 { + if len(o.Options) <= 0 { input, _ := stdin.Read() if input == "" { return errors.New("no options provided, see `gum choose --help`") @@ -33,132 +21,69 @@ func (o Options) Run() error { o.Options = strings.Split(strings.TrimSuffix(input, "\n"), "\n") } - if o.SelectIfOne && len(o.Options) == 1 { - fmt.Println(o.Options[0]) + theme := huh.ThemeCharm() + options := huh.NewOptions(o.Options...) + + theme.Focused.Base.Border(lipgloss.Border{}) + theme.Focused.SelectSelector = 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) + } + + if o.Limit > 1 { + var choices []string + err := huh.NewForm( + huh.NewGroup( + huh.NewMultiSelect[string](). + Options(options...). + Title(o.Header). + Filterable(false). + Height(o.Height). + Limit(o.Limit). + Value(&choices), + ), + ). + WithTheme(theme). + WithShowHelp(false). + Run() + if err != nil { + return err + } + if len(choices) > 0 { + fmt.Println(strings.Join(choices, "\n")) + } return nil } - // We don't need to display prefixes if we are only picking one option. - // Simply displaying the cursor is enough. - if o.Limit == 1 && !o.NoLimit { - o.SelectedPrefix = "" - o.UnselectedPrefix = "" - o.CursorPrefix = "" - } - - // If we've set no limit then we can simply select as many options as there - // are so let's set the limit to the number of options. - if o.NoLimit { - o.Limit = len(o.Options) + 1 - } - - if len(o.Selected) > o.Limit { - return errors.New("number of selected options cannot be greater than the limit") - } - - // Keep track of the selected items. - currentSelected := 0 - // Check if selected items should be used. - hasSelectedItems := len(o.Selected) > 0 - - startingIndex := 0 - currentOrder := 0 - - items := make([]item, len(o.Options)) - - for i, option := range o.Options { - var order int - // Check if the option should be selected. - isSelected := hasSelectedItems && currentSelected < o.Limit && arrayContains(o.Selected, option) - // If the option is selected then increment the current selected count. - if isSelected { - if o.Limit == 1 { - // When the user can choose only one option don't select the option but - // start with the cursor hovering over it. - startingIndex = i - isSelected = false - } else { - currentSelected++ - order = currentOrder - currentOrder++ - } - } - - items[i] = item{text: option, selected: isSelected, order: order} - } - - // Use the pagination model to display the current and total number of - // pages. - pager := paginator.New() - pager.SetTotalPages((len(items) + o.Height - 1) / o.Height) - pager.PerPage = o.Height - pager.Type = paginator.Dots - pager.ActiveDot = subduedStyle.Render("•") - pager.InactiveDot = verySubduedStyle.Render("•") - pager.KeyMap = paginator.KeyMap{} - pager.Page = startingIndex / o.Height - - // Disable Keybindings since we will control it ourselves. - tm, err := tea.NewProgram(model{ - index: startingIndex, - currentOrder: currentOrder, - height: o.Height, - cursor: o.Cursor, - header: o.Header, - selectedPrefix: o.SelectedPrefix, - unselectedPrefix: o.UnselectedPrefix, - cursorPrefix: o.CursorPrefix, - items: items, - limit: o.Limit, - paginator: pager, - cursorStyle: o.CursorStyle.ToLipgloss(), - headerStyle: o.HeaderStyle.ToLipgloss(), - itemStyle: o.ItemStyle.ToLipgloss(), - selectedItemStyle: o.SelectedItemStyle.ToLipgloss(), - numSelected: currentSelected, - hasTimeout: o.Timeout > 0, - timeout: o.Timeout, - }, tea.WithOutput(os.Stderr)).Run() - + var choice string + err := huh.NewForm( + huh.NewGroup( + huh.NewSelect[string](). + Options(options...). + Title(o.Header). + Height(o.Height). + Value(&choice), + ), + ). + WithTheme(theme). + WithShowHelp(false). + Run() if err != nil { - return fmt.Errorf("failed to start tea program: %w", err) + return err } - - m := tm.(model) - if m.aborted { - return exit.ErrAborted - } - - if o.Ordered && o.Limit > 1 { - sort.Slice(m.items, func(i, j int) bool { - return m.items[i].order < m.items[j].order - }) - } - - var s strings.Builder - - for _, item := range m.items { - if item.selected { - s.WriteString(item.text) - s.WriteRune('\n') - } - } - - if isatty.IsTerminal(os.Stdout.Fd()) { - fmt.Print(s.String()) - } else { - fmt.Print(ansi.Strip(s.String())) - } - + fmt.Println(choice) return nil } - -// Check if an array contains a value. -func arrayContains(strArray []string, value string) bool { - for _, str := range strArray { - if str == value { - return true - } - } - return false -} diff --git a/choose/options.go b/choose/options.go index cab9e80..f1fa501 100644 --- a/choose/options.go +++ b/choose/options.go @@ -15,9 +15,9 @@ type Options struct { Height int `help:"Height of the list" default:"10" env:"GUM_CHOOSE_HEIGHT"` Cursor string `help:"Prefix to show on item that corresponds to the cursor position" default:"> " env:"GUM_CHOOSE_CURSOR"` Header string `help:"Header value" default:"" env:"GUM_CHOOSE_HEADER"` - CursorPrefix string `help:"Prefix to show on the cursor item (hidden if limit is 1)" default:"○ " env:"GUM_CHOOSE_CURSOR_PREFIX"` - SelectedPrefix string `help:"Prefix to show on selected items (hidden if limit is 1)" default:"◉ " env:"GUM_CHOOSE_SELECTED_PREFIX"` - UnselectedPrefix string `help:"Prefix to show on unselected items (hidden if limit is 1)" default:"○ " env:"GUM_CHOOSE_UNSELECTED_PREFIX"` + CursorPrefix string `help:"Prefix to show on the cursor item (hidden if limit is 1)" default:"• " env:"GUM_CHOOSE_CURSOR_PREFIX"` + SelectedPrefix string `help:"Prefix to show on selected items (hidden if limit is 1)" default:"✓ " env:"GUM_CHOOSE_SELECTED_PREFIX"` + UnselectedPrefix string `help:"Prefix to show on unselected items (hidden if limit is 1)" default:"• " env:"GUM_CHOOSE_UNSELECTED_PREFIX"` Selected []string `help:"Options that should start as selected" default:"" env:"GUM_CHOOSE_SELECTED"` SelectIfOne bool `help:"Select the given option if there is only one" group:"Selection"` CursorStyle style.Styles `embed:"" prefix:"cursor." set:"defaultForeground=212" envprefix:"GUM_CHOOSE_CURSOR_"` diff --git a/go.mod b/go.mod index 17d5c20..23f0451 100644 --- a/go.mod +++ b/go.mod @@ -1,13 +1,14 @@ module github.com/charmbracelet/gum -go 1.19 +go 1.21 require ( github.com/alecthomas/kong v0.8.1 github.com/alecthomas/mango-kong v0.1.0 - github.com/charmbracelet/bubbles v0.17.1 + github.com/charmbracelet/bubbles v0.17.2-0.20240125161725-fc18779a6ae9 github.com/charmbracelet/bubbletea v0.25.0 github.com/charmbracelet/glamour v0.6.1-0.20230531150759-6d5b52861a9d + github.com/charmbracelet/huh v0.3.1-0.20240125205708-bb9c8f48a9f4 github.com/charmbracelet/lipgloss v0.9.1 github.com/charmbracelet/log v0.3.1 github.com/mattn/go-isatty v0.0.20 @@ -22,6 +23,7 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect + github.com/catppuccin/go v0.2.0 // indirect github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect github.com/dlclark/regexp2 v1.8.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect @@ -40,8 +42,8 @@ require ( github.com/yuin/goldmark-emoji v1.0.1 // indirect golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.1.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.13.0 // indirect - golang.org/x/text v0.13.0 // indirect + golang.org/x/sync v0.6.0 // indirect + golang.org/x/sys v0.16.0 // indirect + golang.org/x/term v0.16.0 // indirect + golang.org/x/text v0.14.0 // indirect ) diff --git a/go.sum b/go.sum index 5508b34..5e06d20 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,5 @@ github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= github.com/alecthomas/chroma/v2 v2.7.0 h1:hm1rY6c/Ob4eGclpQ7X/A3yhqBOZNUTk9q+yhyLIViI= github.com/alecthomas/chroma/v2 v2.7.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY= @@ -6,18 +7,23 @@ github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqr github.com/alecthomas/mango-kong v0.1.0 h1:iFVfP1k1K4qpml3JUQmD5I8MCQYfIvsD9mRdrw7jJC4= github.com/alecthomas/mango-kong v0.1.0/go.mod h1:t+TYVdsONUolf/BwVcm+15eqcdAj15h4Qe9MMFAwwT4= github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4= -github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o= +github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA= +github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc= +github.com/charmbracelet/bubbles v0.17.2-0.20240125161725-fc18779a6ae9 h1:DgM7KYTYQqfv7FmUFsSMgjpzsQgZPToBKANhrbgEGmg= +github.com/charmbracelet/bubbles v0.17.2-0.20240125161725-fc18779a6ae9/go.mod h1:bFPs/pcBrPDNRHK9DOOctOwvdSjRtdA5iFmhdQ/QT7w= github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= github.com/charmbracelet/glamour v0.6.1-0.20230531150759-6d5b52861a9d h1:S4Ejl/M2VrryIgDrDbiuvkwMUDa67/t/H3Wz3i2/vUw= github.com/charmbracelet/glamour v0.6.1-0.20230531150759-6d5b52861a9d/go.mod h1:swCB3CXFsh22H1ESDYdY1tirLiNqCziulDyJ1B6Nt7Q= +github.com/charmbracelet/huh v0.3.1-0.20240125205708-bb9c8f48a9f4 h1:zYLeAfSNeU5o3JrRW1yRMsnOFIlsB2ISEekTWy9cbto= +github.com/charmbracelet/huh v0.3.1-0.20240125205708-bb9c8f48a9f4/go.mod h1:fujUdKX8tC45CCSaRQdw789O6uaCRwx8l2NDyKfC4jA= github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw= @@ -25,6 +31,7 @@ github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3z github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.8.1 h1:6Lcdwya6GjPUNsBct8Lg/yRPwMhABj269AAzdGSiR+0= github.com/dlclark/regexp2 v1.8.1/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= @@ -34,7 +41,9 @@ github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KE github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= @@ -62,6 +71,7 @@ github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1n github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= @@ -69,6 +79,7 @@ github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUc github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y= github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -78,14 +89,15 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=