Skip to content

Commit

Permalink
Limits the number of repos shown to roughly 20.
Browse files Browse the repository at this point in the history
  • Loading branch information
blkt committed May 7, 2024
1 parent 0b5d629 commit 650840f
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 47 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ require (
github.com/alexdrl/zerowater v0.0.3
github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df
github.com/cenkalti/backoff/v4 v4.3.0
github.com/charmbracelet/bubbles v0.17.1
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.10.0
github.com/erikgeiser/promptkit v0.9.0
Expand Down Expand Up @@ -93,7 +94,6 @@ require (
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bluele/gcache v0.0.2 // indirect
github.com/charmbracelet/bubbles v0.17.1 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/containerd/containerd v1.7.15 // indirect
github.com/containerd/log v0.1.0 // indirect
Expand Down Expand Up @@ -150,6 +150,7 @@ require (
github.com/rs/cors v1.10.1 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f // indirect
github.com/secure-systems-lab/go-securesystemslib v0.8.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/sethvargo/go-retry v0.2.4 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -815,6 +815,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
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/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A=
github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=
Expand Down
146 changes: 100 additions & 46 deletions internal/util/cli/multi_select.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,88 +17,142 @@ package cli

import (
"fmt"
"io"
"strings"

"github.com/charmbracelet/bubbles/key"
"github.com/charmbracelet/bubbles/list"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)

type model struct {
choices []string // available choices
cursor int // which item our cursor is pointing at
selected map[int]struct{} // which items are selected
}

// MultiSelect implements the necessary logic to implement an
// interactive multi-select menu for the CLI.
//
// Given a list of string as choices, returns those interactively
// selected by the user.
func MultiSelect(choices []string) ([]string, error) {
model := model{
choices: choices,
selected: make(map[int]struct{}),
items := make([]list.Item, 0, len(choices))
for _, c := range choices {
items = append(items, item{title: c})
}

l := list.New(items, itemDelegate{}, 0, 0)
l.Title = "Select repos to register"
l.Styles.Title = Header
l.AdditionalShortHelpKeys = extraKeys
l.AdditionalFullHelpKeys = extraKeys

height := 30 // 20 + 10, 10 is a magic number to show 20 items
if size := len(items); size < 20 {
height = size + 10
}
model := model{list: l, height: height}
p := tea.NewProgram(model)
if _, err := p.Run(); err != nil {
return nil, err
}

selection := make([]string, 0, len(model.selected))
for i := range model.selected {
selection = append(selection, model.choices[i])
selection := make([]string, 0, len(items))
for _, listItem := range items {
item := listItem.(item)
if item.checked {
selection = append(selection, item.title)
}
}

return selection, nil
}

func (_ model) Init() tea.Cmd {
var (
appStyle = lipgloss.NewStyle().Padding(1, 2)

Check failure on line 68 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

var `appStyle` is unused (unused)
itemStyle = lipgloss.NewStyle().PaddingLeft(4)
selectedItemStyle = lipgloss.NewStyle().PaddingLeft(2).Foreground(SecondaryColor)
)

// item represents the model object for every item in the multi-select
// list.
type item struct {
title string
checked bool
}

func (i item) Title() string { return fmt.Sprintf("[%s] %s", i.checked, i.title) }

Check failure on line 80 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / test / Coverage

fmt.Sprintf format %s has arg i.checked of wrong type bool

Check failure on line 80 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / test / Unit testing

fmt.Sprintf format %s has arg i.checked of wrong type bool

Check failure on line 80 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

printf: fmt.Sprintf format %s has arg i.checked of wrong type bool (govet)
func (i item) Description() string { return "" }

Check failure on line 81 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

unused-receiver: method receiver 'i' is not referenced in method's body, consider removing or renaming it as _ (revive)
func (i item) FilterValue() string { return i.title }

// itemDelegate packs all the logic related to rendering items in TUI
type itemDelegate struct{}

func (d itemDelegate) Height() int { return 1 }

Check failure on line 87 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

unused-receiver: method receiver 'd' is not referenced in method's body, consider removing or renaming it as _ (revive)
func (d itemDelegate) Spacing() int { return 0 }

Check failure on line 88 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

unused-receiver: method receiver 'd' is not referenced in method's body, consider removing or renaming it as _ (revive)
func (d itemDelegate) Update(msg tea.Msg, m *list.Model) tea.Cmd { return nil }

Check failure on line 89 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

unused-parameter: parameter 'msg' seems to be unused, consider removing or renaming it as _ (revive)
func (d itemDelegate) Render(w io.Writer, m list.Model, index int, listItem list.Item) {

Check failure on line 90 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

unused-receiver: method receiver 'd' is not referenced in method's body, consider removing or renaming it as _ (revive)
i, ok := listItem.(item)
if !ok {
return
}

fn := itemStyle.Render
if index == m.Index() {
fn = func(s ...string) string {
return selectedItemStyle.Render("> " + strings.Join(s, " "))
}
}

checked := "[ ]"
if i.checked {
checked = "[x]"
}

fmt.Fprint(w, fn(checked, i.title))
}

// model represents the actual multi-select, with all its rendering,
// navigation, and selection logic.
type model struct {
list list.Model
height int
}

func (m model) Init() tea.Cmd {

Check failure on line 118 in internal/util/cli/multi_select.go

View workflow job for this annotation

GitHub Actions / lint / Go Lint

unused-receiver: method receiver 'm' is not referenced in method's body, consider removing or renaming it as _ (revive)
return nil
}

//nolint:revive // this seems to me like a false positive
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
m.list.SetSize(0, m.height)

case tea.KeyMsg:
switch msg.String() {
case "ctrl+c", "enter", "q": // <C-c>, <enter> and <q> quits
case "ctrl+c", "enter":
return m, tea.Quit
case "up": // <up> and <down> arrows move through selections
if m.cursor > 0 {
m.cursor--
}
case "down": // <up> and <down> arrows move through selections
if m.cursor < len(m.choices)-1 {
m.cursor++
}
case "space", " ": // <space> selects entries
_, ok := m.selected[m.cursor]
if ok {
delete(m.selected, m.cursor)
} else {
m.selected[m.cursor] = struct{}{}
}
case " ":
idx := m.list.Index()
oldItem := m.list.SelectedItem().(item)
cmd := m.list.SetItem(idx, item{
title: oldItem.title,
checked: !oldItem.checked,
})
return m, cmd
}
}

return m, nil
var cmd tea.Cmd
m.list, cmd = m.list.Update(msg)
return m, cmd
}

func (m model) View() string {
s := "Select repositories to register with Minder:\n\n"

for i, choice := range m.choices {
cursor := " "
if m.cursor == i {
cursor = ">"
}

checked := " "
if _, ok := m.selected[i]; ok {
checked = "x"
}
return m.list.View()
}

s += fmt.Sprintf("%s [%s] %s\n", cursor, checked, choice)
func extraKeys() []key.Binding {
return []key.Binding{
key.NewBinding(
key.WithKeys("space"),
key.WithHelp("space", "select item"),
),
}

return s
}

0 comments on commit 650840f

Please sign in to comment.