diff --git a/README.md b/README.md index 33f0af7..29f1dd2 100644 --- a/README.md +++ b/README.md @@ -289,6 +289,34 @@ gum confirm && rm file.txt || echo "File not removed" Shell running gum confirm +#### File + +Prompt the user to select a file from the file tree. + +```bash +EDITOR $(gum file $HOME) +``` + + + + + Shell running gum file + + +#### Pager + +Scroll through a long document with line numbers and a fully customizable viewport. + +```bash +gum pager < README.md +``` + + + + + Shell running gum pager + + #### Spin Display a spinner while running a script or command. The spinner will @@ -306,6 +334,20 @@ gum spin --spinner dot --title "Buying Bubble Gum..." -- sleep 5 Available spinner types include: `line`, `dot`, `minidot`, `jump`, `pulse`, `points`, `globe`, `moon`, `monkey`, `meter`, `hamburger`. +#### Table + +Select a row from some tabular data. + +```bash +gum table < flavors.csv | cut -d ',' -f 1 +``` + + + + + Shell running gum table + + ## Styling #### Style diff --git a/file/command.go b/file/command.go index bdade02..fd6df01 100644 --- a/file/command.go +++ b/file/command.go @@ -17,7 +17,7 @@ func (o Options) Run() error { path, err := filepath.Abs(o.Path) if err != nil { - return err + return fmt.Errorf("file not found: %w", err) } m := model{ @@ -43,7 +43,7 @@ func (o Options) Run() error { tm, err := tea.NewProgram(&m, tea.WithOutput(os.Stderr)).StartReturningModel() if err != nil { - return err + return fmt.Errorf("unable to pick selection: %w", err) } m = tm.(model) diff --git a/file/file.go b/file/file.go index 06e0e10..d1db75a 100644 --- a/file/file.go +++ b/file/file.go @@ -196,30 +196,34 @@ func (m model) View() string { isSymlink := info.Mode()&fs.ModeSymlink != 0 size := humanize.Bytes(uint64(info.Size())) name := f.Name() + if isSymlink { symlinkPath, _ = filepath.EvalSymlinks(filepath.Join(m.path, name)) } + if m.selected == i { selected := fmt.Sprintf(" %s %"+fmt.Sprint(m.fileSizeStyle.GetWidth())+"s %s", info.Mode().String(), size, name) if isSymlink { selected = fmt.Sprintf("%s → %s", selected, symlinkPath) } s.WriteString(m.cursorStyle.Render(m.cursor) + m.selectedStyle.Render(selected)) - } else { - var style = m.fileStyle - if f.IsDir() { - style = m.directoryStyle - } else if isSymlink { - style = m.symlinkStyle - } - - fileName := style.Render(name) - if isSymlink { - fileName = fmt.Sprintf("%s → %s", fileName, symlinkPath) - } - s.WriteString(fmt.Sprintf(" %s %s %s", m.permissionStyle.Render(info.Mode().String()), m.fileSizeStyle.Render(size), fileName)) + s.WriteRune('\n') + continue } - s.WriteString("\n") + + var style = m.fileStyle + if f.IsDir() { + style = m.directoryStyle + } else if isSymlink { + style = m.symlinkStyle + } + + fileName := style.Render(name) + if isSymlink { + fileName = fmt.Sprintf("%s → %s", fileName, symlinkPath) + } + s.WriteString(fmt.Sprintf(" %s %s %s", m.permissionStyle.Render(info.Mode().String()), m.fileSizeStyle.Render(size), fileName)) + s.WriteRune('\n') } return s.String() diff --git a/file/hidden_unix.go b/file/hidden_unix.go index ada959b..a3e2cb1 100644 --- a/file/hidden_unix.go +++ b/file/hidden_unix.go @@ -4,7 +4,7 @@ package file import "strings" -// IsHidden reports whether a file is hidden or not +// IsHidden reports whether a file is hidden or not. func IsHidden(file string) (bool, error) { return strings.HasPrefix(file, "."), nil } diff --git a/file/options.go b/file/options.go index 415fbf4..91228f5 100644 --- a/file/options.go +++ b/file/options.go @@ -16,6 +16,8 @@ type Options struct { DirectoryStyle style.Styles `embed:"" prefix:"directory." help:"The style to use for directories" set:"defaultForeground=99" envprefix:"GUM_FILE_DIRECTORY_"` FileStyle style.Styles `embed:"" prefix:"file." help:"The style to use for files" envprefix:"GUM_FILE_FILE_"` PermissionsStyle style.Styles `embed:"" prefix:"permissions." help:"The style to use for permissions" set:"defaultForeground=244" envprefix:"GUM_FILE_PERMISSIONS_"` - SelectedStyle style.Styles `embed:"" prefix:"selected." help:"The style to use for the selected item" set:"defaultBold=true" set:"defaultForeground=212" envprefix:"GUM_FILE_SELECTED_"` - FileSizeStyle style.Styles `embed:"" prefix:"file-size." help:"The style to use for file sizes" set:"defaultWidth=8" set:"defaultAlign=right" set:"defaultForeground=240" envprefix:"GUM_FILE_FILE_SIZE_"` + //nolint:staticcheck + SelectedStyle style.Styles `embed:"" prefix:"selected." help:"The style to use for the selected item" set:"defaultBold=true" set:"defaultForeground=212" envprefix:"GUM_FILE_SELECTED_"` + //nolint:staticcheck + FileSizeStyle style.Styles `embed:"" prefix:"file-size." help:"The style to use for file sizes" set:"defaultWidth=8" set:"defaultAlign=right" set:"defaultForeground=240" envprefix:"GUM_FILE_FILE_SIZE_"` } diff --git a/go.sum b/go.sum index 4f1b376..88c563a 100644 --- a/go.sum +++ b/go.sum @@ -13,23 +13,15 @@ github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZs github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= 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.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= -github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= -github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= -github.com/charmbracelet/bubbletea v0.22.1 h1:z66q0LWdJNOWEH9zadiAIXp2GN1AWrwNXU8obVY9X24= +github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a h1:/prXWlDbR4CWT1FaTvkU8WhLfpZv3eOrN9PtL8oDdDU= +github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a/go.mod h1:5rZgJTHmgWISQnxnzzIJtQt3GC1bfJfNmr4SEtRDtTQ= github.com/charmbracelet/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0= -<<<<<<< HEAD github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f h1:mbSd0Sdm2wXUWtXJ81o86G9V+9IhddX0qQcGK6bMbKo= github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -||||||| parent of c5917c0 (feat(file): gum file to pick files) -github.com/charmbracelet/bubbletea v0.22.2-0.20221006105051-f406999cba69 h1:GpZktjqyEQjuvFtFb0UlMlbZCOwOhk/bpKb6+quLz+E= -github.com/charmbracelet/bubbletea v0.22.2-0.20221006105051-f406999cba69/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -======= ->>>>>>> c5917c0 (feat(file): gum file to pick files) github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da h1:FGz53GWQRiKQ/5xUsoCCkewSQIC7u81Scaxx2nUy3nM= github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da/go.mod h1:HXz79SMFnF9arKxqeoHWxmo1BhplAH7wehlRhKQIL94= github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= +github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= github.com/charmbracelet/lipgloss v0.6.1-0.20220930064401-ae7c84f7b158 h1:uuY3ti70rfEiLw3rHKSRiJ+cWfq4KWScgjxhoVRf0eE= github.com/charmbracelet/lipgloss v0.6.1-0.20220930064401-ae7c84f7b158/go.mod h1:5EY1dcRQX7kPSA5ssoYjq2qEDhpS4cdtmdYY1OlAdMs= github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= @@ -62,7 +54,6 @@ github.com/microcosm-cc/bluemonday v1.0.19 h1:OI7hoF5FY4pFz2VA//RN8TfM0YJ2dJcl4P github.com/microcosm-cc/bluemonday v1.0.19/go.mod h1:QNzV2UbLK2/53oIIwTOyLUSABMkjZ4tqiyC1g/DyqxE= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= -github.com/muesli/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/mango v0.1.1-0.20220205060214-77e2058169ab h1:m7QFONkzLK0fVXCjwX5tANcnj1yXxTnYQtnfJiY3tcA= @@ -107,7 +98,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/internal/stack/stack.go b/internal/stack/stack.go index af90413..b28fedb 100644 --- a/internal/stack/stack.go +++ b/internal/stack/stack.go @@ -7,7 +7,7 @@ type Stack struct { Length func() int } -// NewStack returns a new stack of integers +// NewStack returns a new stack of integers. func NewStack() Stack { slice := make([]int, 0) return Stack{ diff --git a/pager/command.go b/pager/command.go index 77da6ba..ec9c1b6 100644 --- a/pager/command.go +++ b/pager/command.go @@ -13,12 +13,11 @@ import ( func (o Options) Run() error { vp := viewport.New(o.Style.Width, o.Style.Height) vp.Style = o.Style.ToLipgloss() - var err error if o.Content == "" { stdin, err := stdin.Read() if err != nil { - return err + return fmt.Errorf("unable to read stdin") } if stdin != "" { o.Content = stdin @@ -34,9 +33,9 @@ func (o Options) Run() error { showLineNumbers: o.ShowLineNumbers, lineNumberStyle: o.LineNumberStyle.ToLipgloss(), } + err := tea.NewProgram(model, tea.WithAltScreen()).Start() if err != nil { - return err + return fmt.Errorf("unable to start program: %w", err) } - - return tea.NewProgram(model, tea.WithAltScreen()).Start() + return nil } diff --git a/pager/options.go b/pager/options.go index 9900d82..f8da7ce 100644 --- a/pager/options.go +++ b/pager/options.go @@ -4,6 +4,7 @@ import "github.com/charmbracelet/gum/style" // Options are the options for the pager. type Options struct { + //nolint:staticcheck Style style.Styles `embed:"" help:"Style the pager" set:"defaultBorder=rounded" set:"defaultPadding=0 1" set:"defaultBorderForeground=212" envprefix:"GUM_PAGER_"` HelpStyle style.Styles `embed:"" prefix:"help." help:"Style the help text" set:"defaultForeground=241" envprefix:"GUM_PAGER_HELP_"` Content string `arg:"" optional:"" help:"Display content to scroll"` diff --git a/pager/pager.go b/pager/pager.go index f359a0e..f920074 100644 --- a/pager/pager.go +++ b/pager/pager.go @@ -28,7 +28,7 @@ func (m model) Init() tea.Cmd { func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: - m.viewport.Height = msg.Height - 2 + m.viewport.Height = msg.Height - lipgloss.Height(m.helpStyle.Render("?")) - 1 m.viewport.Width = msg.Width textStyle := lipgloss.NewStyle().Width(m.viewport.Width) var text strings.Builder diff --git a/table/command.go b/table/command.go index 364fa83..1fe4106 100644 --- a/table/command.go +++ b/table/command.go @@ -15,11 +15,11 @@ import ( "github.com/charmbracelet/gum/style" ) -// Run provides a shell script interface for rendering tabular data (CSV) +// Run provides a shell script interface for rendering tabular data (CSV). func (o Options) Run() error { var reader *csv.Reader if o.File != "" { - file, err := os.OpenFile(o.File, os.O_RDONLY, 0600) + file, err := os.Open(o.File) if err != nil { return fmt.Errorf("could not find file at path %s", o.File) } @@ -54,7 +54,7 @@ func (o Options) Run() error { if err != nil { return fmt.Errorf("invalid data provided") } - var columns []table.Column + var columns = make([]table.Column, 0, len(columnNames)) for i, title := range columnNames { width := runewidth.StringWidth(title) @@ -75,7 +75,7 @@ func (o Options) Run() error { Selected: defaultStyles.Selected.Inherit(o.SelectedStyle.ToLipgloss()), } - var rows []table.Row + var rows = make([]table.Row, 0, len(data)) for _, row := range data { rows = append(rows, table.Row(row)) } @@ -99,7 +99,7 @@ func (o Options) Run() error { } m := tm.(model) - fmt.Println(strings.Join([]string(m.selected), string(o.Separator))) + fmt.Println(strings.Join([]string(m.selected), o.Separator)) return nil } diff --git a/table/table.go b/table/table.go index 4c5929a..ce55616 100644 --- a/table/table.go +++ b/table/table.go @@ -8,11 +8,10 @@ // // $ gum table <<< "Flavor,Price\nStrawberry,$0.50\nBanana,$0.99\nCherry,$0.75" // -// Flavor Price -// Strawberry $0.50 -// Banana $0.99 -// Cherry $0.75 -// +// Flavor Price +// Strawberry $0.50 +// Banana $0.99 +// Cherry $0.75 package table import (