aerc/commands/util.go
y0ast 03650474e2 update tcell to v2 and enable TrueColor support
Also update to the tcell v2 PaletteColor api, which should keep the chosen
theme of the user intact.

Note, that if $TRUECOLOR is defined and a truecolor given, aerc will now stop
clipping the value to one of the theme colors.
Generally this is desired behaviour though.
2020-12-18 07:23:22 +01:00

194 lines
3.7 KiB
Go

package commands
import (
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"sort"
"strings"
"time"
"git.sr.ht/~sircmpwn/aerc/lib"
"git.sr.ht/~sircmpwn/aerc/models"
"git.sr.ht/~sircmpwn/aerc/widgets"
"github.com/gdamore/tcell/v2"
"github.com/mitchellh/go-homedir"
)
// QuickTerm is an ephemeral terminal for running a single command and quitting.
func QuickTerm(aerc *widgets.Aerc, args []string, stdin io.Reader) (*widgets.Terminal, error) {
cmd := exec.Command(args[0], args[1:]...)
pipe, err := cmd.StdinPipe()
if err != nil {
return nil, err
}
term, err := widgets.NewTerminal(cmd)
if err != nil {
return nil, err
}
term.OnClose = func(err error) {
if err != nil {
aerc.PushError(" " + err.Error())
// remove the tab on error, otherwise it gets stuck
aerc.RemoveTab(term)
} else {
aerc.PushStatus("Process complete, press any key to close.",
10*time.Second)
term.OnEvent = func(event tcell.Event) bool {
aerc.RemoveTab(term)
return true
}
}
}
term.OnStart = func() {
status := make(chan error, 1)
go func() {
_, err := io.Copy(pipe, stdin)
defer pipe.Close()
status <- err
}()
err := <-status
if err != nil {
aerc.PushError(" " + err.Error())
}
}
return term, nil
}
// CompletePath provides filesystem completions given a starting path.
func CompletePath(path string) []string {
if path == "" {
// default to cwd
cwd, err := os.Getwd()
if err != nil {
return nil
}
path = cwd
}
path, err := homedir.Expand(path)
if err != nil {
return nil
}
// strip trailing slashes, etc.
path = filepath.Clean(path)
if _, err := os.Stat(path); os.IsNotExist(err) {
// if the path doesn't exist, it is likely due to it being a partial path
// in this case, we want to return possible matches (ie /hom* should match
// /home)
matches, err := filepath.Glob(fmt.Sprintf("%s*", path))
if err != nil {
return nil
}
for i, m := range matches {
if isDir(m) {
matches[i] = m + "/"
}
}
sort.Strings(matches)
return matches
}
files := listDir(path, false)
for i, f := range files {
f = filepath.Join(path, f)
if isDir(f) {
f += "/"
}
files[i] = f
}
sort.Strings(files)
return files
}
func isDir(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return info.IsDir()
}
// return all filenames in a directory, optionally including hidden files
func listDir(path string, hidden bool) []string {
f, err := os.Open(path)
if err != nil {
return []string{}
}
files, err := f.Readdirnames(-1) // read all dir names
if err != nil {
return []string{}
}
if hidden {
return files
}
var filtered []string
for _, g := range files {
if !strings.HasPrefix(g, ".") {
filtered = append(filtered, g)
}
}
return filtered
}
// MarkedOrSelected returns either all marked messages if any are marked or the
// selected message instead
func MarkedOrSelected(pm widgets.ProvidesMessages) ([]uint32, error) {
// marked has priority over the selected message
marked, err := pm.MarkedMessages()
if err != nil {
return nil, err
}
if len(marked) > 0 {
return marked, nil
}
msg, err := pm.SelectedMessage()
if err != nil {
return nil, err
}
return []uint32{msg.Uid}, nil
}
// UidsFromMessageInfos extracts a uid slice from a slice of MessageInfos
func UidsFromMessageInfos(msgs []*models.MessageInfo) []uint32 {
uids := make([]uint32, len(msgs))
i := 0
for _, msg := range msgs {
uids[i] = msg.Uid
i++
}
return uids
}
func MsgInfoFromUids(store *lib.MessageStore, uids []uint32) ([]*models.MessageInfo, error) {
infos := make([]*models.MessageInfo, len(uids))
for i, uid := range uids {
var ok bool
infos[i], ok = store.Messages[uid]
if !ok {
return nil, fmt.Errorf("uid not found")
}
}
return infos, nil
}