add interactive mode
when run with no args, interactive mode is launched files in trash are listed, select files, and use r/c to restore/clean them
This commit is contained in:
parent
5467d7a549
commit
2f56ecf40b
28
internal/modes/modes.go
Normal file
28
internal/modes/modes.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
package modes
|
||||||
|
|
||||||
|
type Mode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Trashing Mode = iota + 1
|
||||||
|
Listing
|
||||||
|
Restoring
|
||||||
|
Cleaning
|
||||||
|
Interactive
|
||||||
|
)
|
||||||
|
|
||||||
|
func (m Mode) String() string {
|
||||||
|
switch m {
|
||||||
|
case Trashing:
|
||||||
|
return "Trashing"
|
||||||
|
case Listing:
|
||||||
|
return "Listing"
|
||||||
|
case Restoring:
|
||||||
|
return "Restoring"
|
||||||
|
case Cleaning:
|
||||||
|
return "Cleaning"
|
||||||
|
case Interactive:
|
||||||
|
return "Interactive"
|
||||||
|
default:
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"git.burning.moe/celediel/gt/internal/dirs"
|
"git.burning.moe/celediel/gt/internal/dirs"
|
||||||
"git.burning.moe/celediel/gt/internal/files"
|
"git.burning.moe/celediel/gt/internal/files"
|
||||||
|
"git.burning.moe/celediel/gt/internal/modes"
|
||||||
"git.burning.moe/celediel/gt/internal/trash"
|
"git.burning.moe/celediel/gt/internal/trash"
|
||||||
"github.com/charmbracelet/bubbles/key"
|
"github.com/charmbracelet/bubbles/key"
|
||||||
"github.com/charmbracelet/bubbles/table"
|
"github.com/charmbracelet/bubbles/table"
|
||||||
|
@ -22,7 +23,7 @@ const (
|
||||||
check string = "☑"
|
check string = "☑"
|
||||||
space string = " "
|
space string = " "
|
||||||
woffset int = 13 // why this number, I don't know
|
woffset int = 13 // why this number, I don't know
|
||||||
hoffset int = 5
|
hoffset int = 6
|
||||||
|
|
||||||
// TODO: make these configurable or something
|
// TODO: make these configurable or something
|
||||||
borderbg string = "5"
|
borderbg string = "5"
|
||||||
|
@ -53,11 +54,12 @@ type model struct {
|
||||||
selected []int
|
selected []int
|
||||||
readonly bool
|
readonly bool
|
||||||
termheight int
|
termheight int
|
||||||
|
mode modes.Mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: reconcile trash.Info and files.File into an interface so I can shorten this up
|
// TODO: reconcile trash.Info and files.File into an interface so I can shorten this up
|
||||||
|
|
||||||
func newInfosModel(is trash.Infos, width, height int, readonly, preselected bool) model {
|
func newInfosModel(is trash.Infos, width, height int, readonly, preselected bool, mode modes.Mode) model {
|
||||||
var (
|
var (
|
||||||
fwidth int = int(math.Round(float64(width-woffset) * 0.4))
|
fwidth int = int(math.Round(float64(width-woffset) * 0.4))
|
||||||
owidth int = int(math.Round(float64(width-woffset) * 0.2))
|
owidth int = int(math.Round(float64(width-woffset) * 0.2))
|
||||||
|
@ -70,6 +72,7 @@ func newInfosModel(is trash.Infos, width, height int, readonly, preselected bool
|
||||||
keys: defaultKeyMap(),
|
keys: defaultKeyMap(),
|
||||||
readonly: readonly,
|
readonly: readonly,
|
||||||
termheight: height,
|
termheight: height,
|
||||||
|
mode: mode,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
slices.SortStableFunc(is, trash.SortByTrashedReverse)
|
slices.SortStableFunc(is, trash.SortByTrashedReverse)
|
||||||
|
@ -128,6 +131,7 @@ func newFilesModel(fs files.Files, width, height int, readonly, preselected bool
|
||||||
m = model{
|
m = model{
|
||||||
keys: defaultKeyMap(),
|
keys: defaultKeyMap(),
|
||||||
readonly: readonly,
|
readonly: readonly,
|
||||||
|
mode: modes.Trashing,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -181,6 +185,8 @@ type keyMap struct {
|
||||||
todo key.Binding
|
todo key.Binding
|
||||||
nada key.Binding
|
nada key.Binding
|
||||||
invr key.Binding
|
invr key.Binding
|
||||||
|
rstr key.Binding
|
||||||
|
clen key.Binding
|
||||||
quit key.Binding
|
quit key.Binding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +212,14 @@ func defaultKeyMap() keyMap {
|
||||||
key.WithKeys("i", "ctrl+i"),
|
key.WithKeys("i", "ctrl+i"),
|
||||||
key.WithHelp("i", "invert selection"),
|
key.WithHelp("i", "invert selection"),
|
||||||
),
|
),
|
||||||
|
clen: key.NewBinding(
|
||||||
|
key.WithKeys("c"),
|
||||||
|
key.WithHelp("c", "clean selection"),
|
||||||
|
),
|
||||||
|
rstr: key.NewBinding(
|
||||||
|
key.WithKeys("r"),
|
||||||
|
key.WithHelp("r", "restore selection"),
|
||||||
|
),
|
||||||
quit: key.NewBinding(
|
quit: key.NewBinding(
|
||||||
key.WithKeys("q", "ctrl+c"),
|
key.WithKeys("q", "ctrl+c"),
|
||||||
key.WithHelp("q", "quit"),
|
key.WithHelp("q", "quit"),
|
||||||
|
@ -229,7 +243,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
case key.Matches(msg, m.keys.mark):
|
case key.Matches(msg, m.keys.mark):
|
||||||
m.toggle_item(m.table.Cursor())
|
m.toggle_item(m.table.Cursor())
|
||||||
case key.Matches(msg, m.keys.doit):
|
case key.Matches(msg, m.keys.doit):
|
||||||
if !m.readonly {
|
if !m.readonly && m.mode != modes.Interactive {
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
case key.Matches(msg, m.keys.nada):
|
case key.Matches(msg, m.keys.nada):
|
||||||
|
@ -238,8 +252,14 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
m.select_all()
|
m.select_all()
|
||||||
case key.Matches(msg, m.keys.invr):
|
case key.Matches(msg, m.keys.invr):
|
||||||
m.invert_selection()
|
m.invert_selection()
|
||||||
|
case key.Matches(msg, m.keys.clen):
|
||||||
|
m.mode = modes.Cleaning
|
||||||
|
return m.quit(false)
|
||||||
|
case key.Matches(msg, m.keys.rstr):
|
||||||
|
m.mode = modes.Restoring
|
||||||
|
return m.quit(false)
|
||||||
case key.Matches(msg, m.keys.quit):
|
case key.Matches(msg, m.keys.quit):
|
||||||
return m.quit()
|
return m.quit(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,6 +282,10 @@ func (m model) View() (out string) {
|
||||||
panels = append(panels, m.footer())
|
panels = append(panels, m.footer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.mode != modes.Listing {
|
||||||
|
panels = append([]string{m.header()}, panels...)
|
||||||
|
}
|
||||||
|
|
||||||
out = lipgloss.JoinVertical(lipgloss.Top, panels...)
|
out = lipgloss.JoinVertical(lipgloss.Top, panels...)
|
||||||
return out + n
|
return out + n
|
||||||
}
|
}
|
||||||
|
@ -276,26 +300,51 @@ func (m model) showHelp() string {
|
||||||
fmt.Sprintf("%s %s", darktext.Render(m.keys.quit.Help().Key), darkertext.Render(m.keys.quit.Help().Desc)),
|
fmt.Sprintf("%s %s", darktext.Render(m.keys.quit.Help().Key), darkertext.Render(m.keys.quit.Help().Desc)),
|
||||||
}
|
}
|
||||||
if !m.readonly {
|
if !m.readonly {
|
||||||
|
if m.mode != modes.Interactive {
|
||||||
|
keys = append([]string{
|
||||||
|
fmt.Sprintf("%s %s", darktext.Render(m.keys.doit.Help().Key), darkertext.Render(m.keys.doit.Help().Desc)),
|
||||||
|
}, keys...)
|
||||||
|
}
|
||||||
keys = append([]string{
|
keys = append([]string{
|
||||||
fmt.Sprintf("%s %s", darktext.Render(m.keys.mark.Help().Key), darkertext.Render(m.keys.mark.Help().Desc)),
|
fmt.Sprintf("%s %s", darktext.Render(m.keys.mark.Help().Key), darkertext.Render(m.keys.mark.Help().Desc)),
|
||||||
fmt.Sprintf("%s %s", darktext.Render(m.keys.doit.Help().Key), darkertext.Render(m.keys.doit.Help().Desc)),
|
|
||||||
}, keys...)
|
}, keys...)
|
||||||
}
|
}
|
||||||
return strings.Join(keys, darkesttext.Render(" • "))
|
return strings.Join(keys, darkesttext.Render(" • "))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m model) header() string {
|
||||||
|
var (
|
||||||
|
mode string
|
||||||
|
keys []string = []string{
|
||||||
|
fmt.Sprintf("%s %s", darktext.Render(m.keys.rstr.Help().Key), darkertext.Render(m.keys.rstr.Help().Desc)),
|
||||||
|
fmt.Sprintf("%s %s", darktext.Render(m.keys.clen.Help().Key), darkertext.Render(m.keys.clen.Help().Desc)),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
switch m.mode {
|
||||||
|
case modes.Interactive:
|
||||||
|
mode = strings.Join(keys, darkesttext.Render(" • "))
|
||||||
|
default:
|
||||||
|
mode = m.mode.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%s %s %d files selected", mode, darkesttext.Render("•"), len(m.selected))
|
||||||
|
}
|
||||||
|
|
||||||
func (m model) footer() string {
|
func (m model) footer() string {
|
||||||
return regulartext.Render(m.showHelp())
|
return regulartext.Render(m.showHelp())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m model) quit() (model, tea.Cmd) {
|
func (m model) quit(unselect_all bool) (model, tea.Cmd) {
|
||||||
m.unselect_all()
|
if unselect_all {
|
||||||
|
m.unselect_all()
|
||||||
|
}
|
||||||
m.table.SetStyles(makeUnselectedStyle())
|
m.table.SetStyles(makeUnselectedStyle())
|
||||||
return m, tea.Quit
|
return m, tea.Quit
|
||||||
}
|
}
|
||||||
|
|
||||||
// update_row updates row of `index` with `row`
|
// update_row updates row of `index` with `row`
|
||||||
func (m *model) update_row(index int, selected bool /* , row table.Row */) {
|
func (m *model) update_row(index int, selected bool) {
|
||||||
rows := m.table.Rows()
|
rows := m.table.Rows()
|
||||||
row := rows[index]
|
row := rows[index]
|
||||||
rows[index] = table.Row{
|
rows[index] = table.Row{
|
||||||
|
@ -307,7 +356,6 @@ func (m *model) update_row(index int, selected bool /* , row table.Row */) {
|
||||||
}
|
}
|
||||||
|
|
||||||
m.table.SetRows(rows)
|
m.table.SetRows(rows)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// toggle_item toggles an item's selected state, and returns the state
|
// toggle_item toggles an item's selected state, and returns the state
|
||||||
|
@ -361,15 +409,15 @@ func (m *model) invert_selection() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func InfoTable(is trash.Infos, width, height int, readonly, preselected bool) ([]int, error) {
|
func InfoTable(is trash.Infos, width, height int, readonly, preselected bool, mode modes.Mode) ([]int, modes.Mode, error) {
|
||||||
if endmodel, err := tea.NewProgram(newInfosModel(is, width, height, readonly, preselected)).Run(); err != nil {
|
if endmodel, err := tea.NewProgram(newInfosModel(is, width, height, readonly, preselected, mode)).Run(); err != nil {
|
||||||
return []int{}, err
|
return []int{}, 0, err
|
||||||
} else {
|
} else {
|
||||||
m, ok := endmodel.(model)
|
m, ok := endmodel.(model)
|
||||||
if ok {
|
if ok {
|
||||||
return m.selected, nil
|
return m.selected, m.mode, nil
|
||||||
} else {
|
} else {
|
||||||
return []int{}, fmt.Errorf("model isn't the right type??")
|
return []int{}, 0, fmt.Errorf("model isn't the right type??")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
185
main.go
185
main.go
|
@ -10,6 +10,7 @@ import (
|
||||||
|
|
||||||
"git.burning.moe/celediel/gt/internal/files"
|
"git.burning.moe/celediel/gt/internal/files"
|
||||||
"git.burning.moe/celediel/gt/internal/filter"
|
"git.burning.moe/celediel/gt/internal/filter"
|
||||||
|
"git.burning.moe/celediel/gt/internal/modes"
|
||||||
"git.burning.moe/celediel/gt/internal/tables"
|
"git.burning.moe/celediel/gt/internal/tables"
|
||||||
"git.burning.moe/celediel/gt/internal/trash"
|
"git.burning.moe/celediel/gt/internal/trash"
|
||||||
|
|
||||||
|
@ -23,6 +24,8 @@ const (
|
||||||
appname string = "gt"
|
appname string = "gt"
|
||||||
appdesc string = "xdg trash cli"
|
appdesc string = "xdg trash cli"
|
||||||
appversion string = "v0.0.1"
|
appversion string = "v0.0.1"
|
||||||
|
yes rune = 'y'
|
||||||
|
no rune = 'n'
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -103,59 +106,29 @@ var (
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if confirm(fmt.Sprintf("trash %d selected files?", len(selected))) {
|
return confirm_trash(selected)
|
||||||
tfs := make([]string, 0, len(selected))
|
|
||||||
for _, file := range selected {
|
|
||||||
log.Debugf("gonna trash %s", file.Filename())
|
|
||||||
tfs = append(tfs, file.Filename())
|
|
||||||
}
|
|
||||||
|
|
||||||
trashed, err := trash.TrashFiles(trashDir, tfs...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("trashed %d files\n", trashed)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("not doing anything\n")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// action launches interactive mode if run without args, or trashes files as args
|
||||||
action = func(ctx *cli.Context) error {
|
action = func(ctx *cli.Context) error {
|
||||||
var (
|
var (
|
||||||
fls trash.Infos
|
err error
|
||||||
indicies []int
|
|
||||||
err error
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if f == nil {
|
if f == nil {
|
||||||
f, err = filter.New(o, b, a, g, p, ung, unp, ctx.Args().Slice()...)
|
f, err = filter.New(o, b, a, g, p, ung, unp)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fls, err = trash.FindFiles(trashDir, ogdir, f)
|
if len(ctx.Args().Slice()) != 0 {
|
||||||
if err != nil {
|
f.AddFileNames(ctx.Args().Slice()...)
|
||||||
return err
|
return do_trash.Action(ctx)
|
||||||
|
} else {
|
||||||
|
return interactive_mode()
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(fls) <= 0 {
|
|
||||||
log.Printf("no files to show")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
indicies, err = tables.InfoTable(fls, termwidth, termheight, false, true)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, i := range indicies {
|
|
||||||
log.Printf("gonna do something with %s", fls[i].Name())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
do_list = &cli.Command{
|
do_list = &cli.Command{
|
||||||
|
@ -185,7 +158,7 @@ var (
|
||||||
}
|
}
|
||||||
|
|
||||||
// display them
|
// display them
|
||||||
_, err = tables.InfoTable(fls, termwidth, termheight, true, false)
|
_, _, err = tables.InfoTable(fls, termwidth, termheight, true, false, modes.Listing)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
},
|
},
|
||||||
|
@ -209,7 +182,7 @@ var (
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
indices, err := tables.InfoTable(fls, termwidth, termheight, false, !f.Blank())
|
indices, _, err := tables.InfoTable(fls, termwidth, termheight, false, !f.Blank(), modes.Restoring)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -223,18 +196,7 @@ var (
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if confirm(fmt.Sprintf("restore %d selected files?", len(selected))) {
|
return confirm_restore(selected)
|
||||||
log.Info("doing the thing")
|
|
||||||
restored, err := trash.Restore(selected)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("restored %d files before error %s", restored, err)
|
|
||||||
}
|
|
||||||
fmt.Printf("restored %d files\n", restored)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("not doing anything\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +215,7 @@ var (
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
indices, err := tables.InfoTable(fls, termwidth, termheight, false, !f.Blank())
|
indices, _, err := tables.InfoTable(fls, termwidth, termheight, false, !f.Blank(), modes.Cleaning)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -267,18 +229,7 @@ var (
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if confirm(fmt.Sprintf("remove %d selected files permanently from the trash?", len(selected))) &&
|
return confirm_clean(selected)
|
||||||
confirm(fmt.Sprintf("really remove all these %d selected files permanently from the trash forever??", len(selected))) {
|
|
||||||
log.Info("gonna remove some files forever")
|
|
||||||
removed, err := trash.Remove(selected)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("removed %d files before error %s", removed, err)
|
|
||||||
}
|
|
||||||
fmt.Printf("removed %d files\n", removed)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("not doing anything\n")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,6 +332,106 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func interactive_mode() error {
|
||||||
|
var (
|
||||||
|
fls trash.Infos
|
||||||
|
indicies []int
|
||||||
|
mode modes.Mode
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
fls, err = trash.FindFiles(trashDir, ogdir, f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fls) <= 0 {
|
||||||
|
log.Printf("no files to show")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
indicies, mode, err = tables.InfoTable(fls, termwidth, termheight, false, false, modes.Interactive)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var selected trash.Infos
|
||||||
|
for _, i := range indicies {
|
||||||
|
selected = append(selected, fls[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case modes.Cleaning:
|
||||||
|
for _, file := range selected {
|
||||||
|
log.Debugf("gonna clean %s", file.Name())
|
||||||
|
}
|
||||||
|
if err := confirm_clean(selected); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case modes.Restoring:
|
||||||
|
for _, file := range selected {
|
||||||
|
log.Debugf("gonna restore %s", file.Name())
|
||||||
|
}
|
||||||
|
if err := confirm_restore(selected); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case modes.Interactive:
|
||||||
|
return nil
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("got bad mode %s", mode)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirm_restore(is trash.Infos) error {
|
||||||
|
if confirm(fmt.Sprintf("restore %d selected files?", len(is))) {
|
||||||
|
log.Info("doing the thing")
|
||||||
|
restored, err := trash.Restore(is)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("restored %d files before error %s", restored, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("restored %d files\n", restored)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("not doing anything\n")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirm_clean(is trash.Infos) error {
|
||||||
|
if confirm(fmt.Sprintf("remove %d selected files permanently from the trash?", len(is))) &&
|
||||||
|
confirm(fmt.Sprintf("really remove all these %d selected files permanently from the trash forever??", len(is))) {
|
||||||
|
log.Info("gonna remove some files forever")
|
||||||
|
removed, err := trash.Remove(is)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("removed %d files before error %s", removed, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("removed %d files\n", removed)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("not doing anything\n")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func confirm_trash(fs files.Files) error {
|
||||||
|
if confirm(fmt.Sprintf("trash %d selected files?", len(fs))) {
|
||||||
|
tfs := make([]string, 0, len(fs))
|
||||||
|
for _, file := range fs {
|
||||||
|
log.Debugf("gonna trash %s", file.Filename())
|
||||||
|
tfs = append(tfs, file.Filename())
|
||||||
|
}
|
||||||
|
|
||||||
|
trashed, err := trash.TrashFiles(trashDir, tfs...)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("trashed %d files\n", trashed)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("not doing anything\n")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func confirm(prompt string) bool {
|
func confirm(prompt string) bool {
|
||||||
// TODO: handle errors better
|
// TODO: handle errors better
|
||||||
// switch stdin into 'raw' mode
|
// switch stdin into 'raw' mode
|
||||||
|
@ -394,7 +445,7 @@ func confirm(prompt string) bool {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
fmt.Printf("%s [y/n]: ", prompt)
|
fmt.Printf("%s [%s/%s]: ", prompt, string(yes), string(no))
|
||||||
|
|
||||||
// read one byte from stdin
|
// read one byte from stdin
|
||||||
b := make([]byte, 1)
|
b := make([]byte, 1)
|
||||||
|
|
Loading…
Reference in a new issue