mirror of
https://github.com/charmbracelet/gum
synced 2024-05-04 15:33:14 +02:00
feat(file): gum file to pick files
This commit is contained in:
parent
430ab459d7
commit
2bea4dc030
58
file/command.go
Normal file
58
file/command.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/gum/internal/stack"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Run is the interface to picking a file.
|
||||||
|
func (o Options) Run() error {
|
||||||
|
if o.Path == "" {
|
||||||
|
o.Path = "."
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := filepath.Abs(o.Path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m := model{
|
||||||
|
path: path,
|
||||||
|
cursor: o.Cursor,
|
||||||
|
selected: 0,
|
||||||
|
showHidden: o.All,
|
||||||
|
autoHeight: o.Height == 0,
|
||||||
|
height: o.Height,
|
||||||
|
max: 0,
|
||||||
|
min: 0,
|
||||||
|
selectedStack: stack.NewStack(),
|
||||||
|
minStack: stack.NewStack(),
|
||||||
|
maxStack: stack.NewStack(),
|
||||||
|
cursorStyle: o.CursorStyle.ToLipgloss(),
|
||||||
|
symlinkStyle: o.SymlinkStyle.ToLipgloss(),
|
||||||
|
directoryStyle: o.DirectoryStyle.ToLipgloss(),
|
||||||
|
fileStyle: o.FileStyle.ToLipgloss(),
|
||||||
|
permissionStyle: o.PermissionsStyle.ToLipgloss(),
|
||||||
|
selectedStyle: o.SelectedStyle.ToLipgloss(),
|
||||||
|
fileSizeStyle: o.FileSizeStyle.ToLipgloss(),
|
||||||
|
}
|
||||||
|
|
||||||
|
tm, err := tea.NewProgram(&m, tea.WithOutput(os.Stderr)).StartReturningModel()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
m = tm.(model)
|
||||||
|
|
||||||
|
if m.path == "" {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(m.path)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
226
file/file.go
Normal file
226
file/file.go
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
// Package file provides an interface to pick a file from a folder (tree).
|
||||||
|
// The user is provided a file manager-like interface to navigate, to
|
||||||
|
// select a file.
|
||||||
|
//
|
||||||
|
// Let's pick a file from the current directory:
|
||||||
|
//
|
||||||
|
// $ gum file
|
||||||
|
// $ gum file .
|
||||||
|
//
|
||||||
|
// Let's pick a file from the home directory:
|
||||||
|
//
|
||||||
|
// $ gum file $HOME
|
||||||
|
package file
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/charmbracelet/gum/internal/stack"
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
"github.com/dustin/go-humanize"
|
||||||
|
)
|
||||||
|
|
||||||
|
const marginBottom = 5
|
||||||
|
|
||||||
|
type model struct {
|
||||||
|
quitting bool
|
||||||
|
path string
|
||||||
|
files []os.DirEntry
|
||||||
|
showHidden bool
|
||||||
|
|
||||||
|
selected int
|
||||||
|
selectedStack stack.Stack
|
||||||
|
|
||||||
|
min int
|
||||||
|
max int
|
||||||
|
maxStack stack.Stack
|
||||||
|
minStack stack.Stack
|
||||||
|
height int
|
||||||
|
autoHeight bool
|
||||||
|
|
||||||
|
cursor string
|
||||||
|
cursorStyle lipgloss.Style
|
||||||
|
symlinkStyle lipgloss.Style
|
||||||
|
directoryStyle lipgloss.Style
|
||||||
|
fileStyle lipgloss.Style
|
||||||
|
permissionStyle lipgloss.Style
|
||||||
|
selectedStyle lipgloss.Style
|
||||||
|
fileSizeStyle lipgloss.Style
|
||||||
|
}
|
||||||
|
|
||||||
|
type readDirMsg []os.DirEntry
|
||||||
|
|
||||||
|
func readDir(path string, showHidden bool) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
dirEntries, err := os.ReadDir(path)
|
||||||
|
if err != nil {
|
||||||
|
return tea.Quit
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(dirEntries, func(i, j int) bool {
|
||||||
|
if dirEntries[i].IsDir() == dirEntries[j].IsDir() {
|
||||||
|
return dirEntries[i].Name() < dirEntries[j].Name()
|
||||||
|
}
|
||||||
|
return dirEntries[i].IsDir()
|
||||||
|
})
|
||||||
|
|
||||||
|
if showHidden {
|
||||||
|
return readDirMsg(dirEntries)
|
||||||
|
}
|
||||||
|
|
||||||
|
var sanitizedDirEntries []fs.DirEntry
|
||||||
|
for _, dirEntry := range dirEntries {
|
||||||
|
isHidden, _ := IsHidden(dirEntry.Name())
|
||||||
|
if isHidden {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sanitizedDirEntries = append(sanitizedDirEntries, dirEntry)
|
||||||
|
}
|
||||||
|
return readDirMsg(sanitizedDirEntries)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) Init() tea.Cmd {
|
||||||
|
return readDir(m.path, m.showHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case readDirMsg:
|
||||||
|
m.files = msg
|
||||||
|
case tea.WindowSizeMsg:
|
||||||
|
if m.autoHeight {
|
||||||
|
m.height = msg.Height - marginBottom
|
||||||
|
}
|
||||||
|
m.max = m.height
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "g":
|
||||||
|
m.selected = 0
|
||||||
|
m.min = 0
|
||||||
|
m.max = m.height - 1
|
||||||
|
case "G":
|
||||||
|
m.selected = len(m.files) - 1
|
||||||
|
m.min = len(m.files) - m.height
|
||||||
|
m.max = len(m.files) - 1
|
||||||
|
case "j", "down":
|
||||||
|
m.selected++
|
||||||
|
if m.selected >= len(m.files) {
|
||||||
|
m.selected = len(m.files) - 1
|
||||||
|
}
|
||||||
|
if m.selected > m.max {
|
||||||
|
m.min++
|
||||||
|
m.max++
|
||||||
|
}
|
||||||
|
case "k", "up":
|
||||||
|
m.selected--
|
||||||
|
if m.selected < 0 {
|
||||||
|
m.selected = 0
|
||||||
|
}
|
||||||
|
if m.selected < m.min {
|
||||||
|
m.min--
|
||||||
|
m.max--
|
||||||
|
}
|
||||||
|
case "ctrl+c", "q":
|
||||||
|
m.path = ""
|
||||||
|
m.quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
case "backspace", "h", "left":
|
||||||
|
m.path = filepath.Dir(m.path)
|
||||||
|
if m.selectedStack.Length() > 0 {
|
||||||
|
m.selected, m.min, m.max = m.popView()
|
||||||
|
} else {
|
||||||
|
m.selected = 0
|
||||||
|
m.min = 0
|
||||||
|
m.max = m.height - 1
|
||||||
|
}
|
||||||
|
return m, readDir(m.path, m.showHidden)
|
||||||
|
case "l", "right", "enter":
|
||||||
|
if len(m.files) == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if !m.files[m.selected].IsDir() {
|
||||||
|
if msg.String() == "enter" {
|
||||||
|
m.path = filepath.Join(m.path, m.files[m.selected].Name())
|
||||||
|
m.quitting = true
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m.path = filepath.Join(m.path, m.files[m.selected].Name())
|
||||||
|
m.pushView()
|
||||||
|
m.selected = 0
|
||||||
|
m.min = 0
|
||||||
|
m.max = m.height - 1
|
||||||
|
return m, readDir(m.path, m.showHidden)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) pushView() {
|
||||||
|
m.minStack.Push(m.min)
|
||||||
|
m.maxStack.Push(m.max)
|
||||||
|
m.selectedStack.Push(m.selected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) popView() (int, int, int) {
|
||||||
|
return m.selectedStack.Pop(), m.minStack.Pop(), m.maxStack.Pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m model) View() string {
|
||||||
|
if m.quitting {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if len(m.files) == 0 {
|
||||||
|
return "Bummer. No files found."
|
||||||
|
}
|
||||||
|
var s strings.Builder
|
||||||
|
|
||||||
|
for i, f := range m.files {
|
||||||
|
if i < m.min {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i > m.max {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
var symlinkPath string
|
||||||
|
info, _ := f.Info()
|
||||||
|
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.WriteString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.String()
|
||||||
|
}
|
10
file/hidden_unix.go
Normal file
10
file/hidden_unix.go
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
//go:build !windows
|
||||||
|
|
||||||
|
package file
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// IsHidden reports whether a file is hidden or not
|
||||||
|
func IsHidden(file string) (bool, error) {
|
||||||
|
return strings.HasPrefix(file, "."), nil
|
||||||
|
}
|
21
file/options.go
Normal file
21
file/options.go
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package file
|
||||||
|
|
||||||
|
import "github.com/charmbracelet/gum/style"
|
||||||
|
|
||||||
|
// Options are the options for the file command.
|
||||||
|
type Options struct {
|
||||||
|
// Path is the path to the folder / directory to begin traversing.
|
||||||
|
Path string `arg:"" optional:"" name:"path" help:"The path to the folder to begin traversing"`
|
||||||
|
// Cursor is the character to display in front of the current selected items.
|
||||||
|
Cursor string `short:"c" help:"The cursor character" default:">"`
|
||||||
|
All bool `short:"a" help:"Show hidden and 'dot' files" default:"true"`
|
||||||
|
|
||||||
|
Height int `help:"Maximum number of files to display" default:"0"`
|
||||||
|
CursorStyle style.Styles `embed:"" prefix:"cursor." help:"The cursor style" set:"defaultForeground=212" envprefix:"GUM_FILE_CURSOR_"`
|
||||||
|
SymlinkStyle style.Styles `embed:"" prefix:"symlink." help:"The style to use for symlinks" set:"defaultForeground=36" envprefix:"GUM_FILE_SYMLINK_"`
|
||||||
|
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_"`
|
||||||
|
}
|
3
go.mod
3
go.mod
|
@ -8,7 +8,8 @@ require (
|
||||||
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a
|
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a
|
||||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f
|
github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f
|
||||||
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da
|
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da
|
||||||
github.com/charmbracelet/lipgloss v0.6.0
|
github.com/charmbracelet/lipgloss v0.6.1-0.20220930064401-ae7c84f7b158
|
||||||
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/mattn/go-runewidth v0.0.14
|
github.com/mattn/go-runewidth v0.0.14
|
||||||
github.com/muesli/roff v0.1.0
|
github.com/muesli/roff v0.1.0
|
||||||
github.com/muesli/termenv v0.13.0
|
github.com/muesli/termenv v0.13.0
|
||||||
|
|
26
go.sum
26
go.sum
|
@ -13,16 +13,25 @@ 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/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 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||||
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
|
||||||
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a h1:/prXWlDbR4CWT1FaTvkU8WhLfpZv3eOrN9PtL8oDdDU=
|
github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og=
|
||||||
github.com/charmbracelet/bubbles v0.14.1-0.20221006154229-d1775121146a/go.mod h1:5rZgJTHmgWISQnxnzzIJtQt3GC1bfJfNmr4SEtRDtTQ=
|
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/bubbletea v0.22.1/go.mod h1:8/7hVvbPN6ZZPkczLiB8YpLkLJ0n7DMho5Wvfd2X1C0=
|
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 h1:mbSd0Sdm2wXUWtXJ81o86G9V+9IhddX0qQcGK6bMbKo=
|
||||||
github.com/charmbracelet/bubbletea v0.22.2-0.20221007181357-2696b2f3399f/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU=
|
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 h1:FGz53GWQRiKQ/5xUsoCCkewSQIC7u81Scaxx2nUy3nM=
|
||||||
github.com/charmbracelet/glamour v0.5.1-0.20220727184942-e70ff2d969da/go.mod h1:HXz79SMFnF9arKxqeoHWxmo1BhplAH7wehlRhKQIL94=
|
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/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao=
|
||||||
github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY=
|
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=
|
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -30,6 +39,8 @@ 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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||||
|
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||||
|
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||||
github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
|
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/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
|
@ -51,6 +62,7 @@ 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/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 h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34=
|
||||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho=
|
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 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
|
||||||
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
|
||||||
github.com/muesli/mango v0.1.1-0.20220205060214-77e2058169ab h1:m7QFONkzLK0fVXCjwX5tANcnj1yXxTnYQtnfJiY3tcA=
|
github.com/muesli/mango v0.1.1-0.20220205060214-77e2058169ab h1:m7QFONkzLK0fVXCjwX5tANcnj1yXxTnYQtnfJiY3tcA=
|
||||||
|
@ -76,9 +88,12 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
|
||||||
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI=
|
||||||
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||||
|
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
|
||||||
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
|
github.com/yuin/goldmark v1.4.4 h1:zNWRjYUW32G9KirMXYHQHVNFkXvMI7LpgNW2AgYAoIs=
|
||||||
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
github.com/yuin/goldmark v1.4.4/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||||
|
@ -92,6 +107,7 @@ 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-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-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-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 h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
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=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
|
28
gum.go
28
gum.go
|
@ -6,6 +6,7 @@ import (
|
||||||
"github.com/charmbracelet/gum/choose"
|
"github.com/charmbracelet/gum/choose"
|
||||||
"github.com/charmbracelet/gum/completion"
|
"github.com/charmbracelet/gum/completion"
|
||||||
"github.com/charmbracelet/gum/confirm"
|
"github.com/charmbracelet/gum/confirm"
|
||||||
|
"github.com/charmbracelet/gum/file"
|
||||||
"github.com/charmbracelet/gum/filter"
|
"github.com/charmbracelet/gum/filter"
|
||||||
"github.com/charmbracelet/gum/format"
|
"github.com/charmbracelet/gum/format"
|
||||||
"github.com/charmbracelet/gum/input"
|
"github.com/charmbracelet/gum/input"
|
||||||
|
@ -56,6 +57,20 @@ type Gum struct {
|
||||||
//
|
//
|
||||||
Confirm confirm.Options `cmd:"" help:"Ask a user to confirm an action"`
|
Confirm confirm.Options `cmd:"" help:"Ask a user to confirm an action"`
|
||||||
|
|
||||||
|
// File provides an interface to pick a file from a folder (tree).
|
||||||
|
// The user is provided a file manager-like interface to navigate, to
|
||||||
|
// select a file.
|
||||||
|
//
|
||||||
|
// Let's pick a file from the current directory:
|
||||||
|
//
|
||||||
|
// $ gum file
|
||||||
|
// $ gum file .
|
||||||
|
//
|
||||||
|
// Let's pick a file from the home directory:
|
||||||
|
//
|
||||||
|
// $ gum file $HOME
|
||||||
|
File file.Options `cmd:"" help:"Pick a file from a folder"`
|
||||||
|
|
||||||
// Filter provides a fuzzy searching text input to allow filtering a list of
|
// Filter provides a fuzzy searching text input to allow filtering a list of
|
||||||
// options to select one option.
|
// options to select one option.
|
||||||
//
|
//
|
||||||
|
@ -106,6 +121,19 @@ type Gum struct {
|
||||||
// https://github.com/charmbracelet/bubbles/tree/master/viewport
|
// https://github.com/charmbracelet/bubbles/tree/master/viewport
|
||||||
//
|
//
|
||||||
// It allows the user to scroll through content like a pager.
|
// It allows the user to scroll through content like a pager.
|
||||||
|
//
|
||||||
|
// ╭────────────────────────────────────────────────╮
|
||||||
|
// │ 1 │ Gum Pager │
|
||||||
|
// │ 2 │ ========= │
|
||||||
|
// │ 3 │ │
|
||||||
|
// │ 4 │ ``` │
|
||||||
|
// │ 5 │ gum pager --height 10 --width 25 < text │
|
||||||
|
// │ 6 │ ``` │
|
||||||
|
// │ 7 │ │
|
||||||
|
// │ 8 │ │
|
||||||
|
// ╰────────────────────────────────────────────────╯
|
||||||
|
// ↑/↓: Navigate • q: Quit
|
||||||
|
//
|
||||||
Pager pager.Options `cmd:"" help:"Scroll through a file"`
|
Pager pager.Options `cmd:"" help:"Scroll through a file"`
|
||||||
|
|
||||||
// Spin provides a shell script interface for the spinner bubble.
|
// Spin provides a shell script interface for the spinner bubble.
|
||||||
|
|
26
internal/stack/stack.go
Normal file
26
internal/stack/stack.go
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
package stack
|
||||||
|
|
||||||
|
// Stack is a stack interface for integers.
|
||||||
|
type Stack struct {
|
||||||
|
Push func(int)
|
||||||
|
Pop func() int
|
||||||
|
Length func() int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewStack returns a new stack of integers
|
||||||
|
func NewStack() Stack {
|
||||||
|
slice := make([]int, 0)
|
||||||
|
return Stack{
|
||||||
|
Push: func(i int) {
|
||||||
|
slice = append(slice, i)
|
||||||
|
},
|
||||||
|
Pop: func() int {
|
||||||
|
res := slice[len(slice)-1]
|
||||||
|
slice = slice[:len(slice)-1]
|
||||||
|
return res
|
||||||
|
},
|
||||||
|
Length: func() int {
|
||||||
|
return len(slice)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
8
main.go
8
main.go
|
@ -53,13 +53,21 @@ func main() {
|
||||||
}),
|
}),
|
||||||
kong.Vars{
|
kong.Vars{
|
||||||
"version": version,
|
"version": version,
|
||||||
|
"defaultHeight": "0",
|
||||||
|
"defaultWidth": "0",
|
||||||
|
"defaultAlign": "left",
|
||||||
"defaultBorder": "none",
|
"defaultBorder": "none",
|
||||||
"defaultBorderForeground": "",
|
"defaultBorderForeground": "",
|
||||||
|
"defaultBorderBackground": "",
|
||||||
"defaultBackground": "",
|
"defaultBackground": "",
|
||||||
"defaultForeground": "",
|
"defaultForeground": "",
|
||||||
"defaultMargin": "0 0",
|
"defaultMargin": "0 0",
|
||||||
"defaultPadding": "0 0",
|
"defaultPadding": "0 0",
|
||||||
"defaultUnderline": "false",
|
"defaultUnderline": "false",
|
||||||
|
"defaultBold": "false",
|
||||||
|
"defaultFaint": "false",
|
||||||
|
"defaultItalic": "false",
|
||||||
|
"defaultStrikethrough": "false",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err := ctx.Run(); err != nil {
|
if err := ctx.Run(); err != nil {
|
||||||
|
|
|
@ -23,20 +23,20 @@ type Styles struct {
|
||||||
|
|
||||||
// Border
|
// Border
|
||||||
Border string `help:"Border Style" enum:"none,hidden,normal,rounded,thick,double" default:"${defaultBorder}" group:"Style Flags" env:"BORDER"`
|
Border string `help:"Border Style" enum:"none,hidden,normal,rounded,thick,double" default:"${defaultBorder}" group:"Style Flags" env:"BORDER"`
|
||||||
BorderBackground string `help:"Border Background Color" group:"Style Flags" env:"BORDER_BACKGROUND"`
|
BorderBackground string `help:"Border Background Color" group:"Style Flags" default:"${defaultBorderBackground}" env:"BORDER_BACKGROUND"`
|
||||||
BorderForeground string `help:"Border Foreground Color" group:"Style Flags" default:"${defaultBorderForeground}" env:"BORDER_FOREGROUND"`
|
BorderForeground string `help:"Border Foreground Color" group:"Style Flags" default:"${defaultBorderForeground}" env:"BORDER_FOREGROUND"`
|
||||||
|
|
||||||
// Layout
|
// Layout
|
||||||
Align string `help:"Text Alignment" enum:"left,center,right,bottom,middle,top" default:"left" group:"Style Flags" env:"ALIGN"`
|
Align string `help:"Text Alignment" enum:"left,center,right,bottom,middle,top" default:"${defaultAlign}" group:"Style Flags" env:"ALIGN"`
|
||||||
Height int `help:"Text height" group:"Style Flags" env:"HEIGHT"`
|
Height int `help:"Text height" default:"${defaultHeight}" group:"Style Flags" env:"HEIGHT"`
|
||||||
Width int `help:"Text width" group:"Style Flags" env:"WIDTH"`
|
Width int `help:"Text width" default:"${defaultWidth}" group:"Style Flags" env:"WIDTH"`
|
||||||
Margin string `help:"Text margin" default:"${defaultMargin}" group:"Style Flags" env:"MARGIN"`
|
Margin string `help:"Text margin" default:"${defaultMargin}" group:"Style Flags" env:"MARGIN"`
|
||||||
Padding string `help:"Text padding" default:"${defaultPadding}" group:"Style Flags" env:"PADDING"`
|
Padding string `help:"Text padding" default:"${defaultPadding}" group:"Style Flags" env:"PADDING"`
|
||||||
|
|
||||||
// Format
|
// Format
|
||||||
Bold bool `help:"Bold text" group:"Style Flags" env:"BOLD"`
|
Bold bool `help:"Bold text" default:"${defaultBold}" group:"Style Flags" env:"BOLD"`
|
||||||
Faint bool `help:"Faint text" group:"Style Flags" env:"FAINT"`
|
Faint bool `help:"Faint text" default:"${defaultFaint}" group:"Style Flags" env:"FAINT"`
|
||||||
Italic bool `help:"Italicize text" group:"Style Flags" env:"ITALIC"`
|
Italic bool `help:"Italicize text" default:"${defaultItalic}" group:"Style Flags" env:"ITALIC"`
|
||||||
Strikethrough bool `help:"Strikethrough text" group:"Style Flags" env:"STRIKETHROUGH"`
|
Strikethrough bool `help:"Strikethrough text" default:"${defaultStrikethrough}" group:"Style Flags" env:"STRIKETHROUGH"`
|
||||||
Underline bool `help:"Underline text" default:"${defaultUnderline}" group:"Style Flags" env:"UNDERLINE"`
|
Underline bool `help:"Underline text" default:"${defaultUnderline}" group:"Style Flags" env:"UNDERLINE"`
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue