aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Jarry <robin@jarry.cc>2023-10-22 23:23:18 +0200
committerRobin Jarry <robin@jarry.cc>2023-10-28 19:25:10 +0200
commitabe228b14d97d8d47e8ff4406de387fac45cfe68 (patch)
tree56117403d1ae32d7253f86bab01a944a3cf225b9
parent30851656591293ed2e19340ab78c937855a11143 (diff)
downloadaerc-abe228b14d97d8d47e8ff4406de387fac45cfe68.tar.gz
aerc-abe228b14d97d8d47e8ff4406de387fac45cfe68.zip
commands: use completion from go-opt
Implement command completion with complete struct field tags from the get-opt library introduced earlier. Changelog-changed: Improved command completion. Signed-off-by: Robin Jarry <robin@jarry.cc> Reviewed-by: Koni Marti <koni.marti@gmail.com> Tested-by: Moritz Poldrack <moritz@poldrack.dev> Tested-by: Inwit <inwit@sindominio.net>
-rw-r--r--commands/account/cf.go6
-rw-r--r--commands/account/check-mail.go4
-rw-r--r--commands/account/clear.go4
-rw-r--r--commands/account/compose.go11
-rw-r--r--commands/account/connection.go4
-rw-r--r--commands/account/expand-folder.go4
-rw-r--r--commands/account/export-mbox.go6
-rw-r--r--commands/account/import-mbox.go7
-rw-r--r--commands/account/mkdir.go29
-rw-r--r--commands/account/next-folder.go4
-rw-r--r--commands/account/next-result.go4
-rw-r--r--commands/account/next.go4
-rw-r--r--commands/account/recover.go7
-rw-r--r--commands/account/rmdir.go4
-rw-r--r--commands/account/search.go41
-rw-r--r--commands/account/select.go4
-rw-r--r--commands/account/sort.go56
-rw-r--r--commands/account/split.go4
-rw-r--r--commands/account/view.go4
-rw-r--r--commands/cd.go7
-rw-r--r--commands/choose.go4
-rw-r--r--commands/commands.go164
-rw-r--r--commands/compose/abort.go4
-rw-r--r--commands/compose/attach-key.go4
-rw-r--r--commands/compose/attach.go13
-rw-r--r--commands/compose/cc-bcc.go4
-rw-r--r--commands/compose/detach.go7
-rw-r--r--commands/compose/edit.go4
-rw-r--r--commands/compose/encrypt.go4
-rw-r--r--commands/compose/header.go6
-rw-r--r--commands/compose/multipart.go7
-rw-r--r--commands/compose/next-field.go4
-rw-r--r--commands/compose/postpone.go18
-rw-r--r--commands/compose/send.go19
-rw-r--r--commands/compose/sign.go4
-rw-r--r--commands/compose/switch.go7
-rw-r--r--commands/ct.go10
-rw-r--r--commands/eml.go7
-rw-r--r--commands/exec.go4
-rw-r--r--commands/help.go6
-rw-r--r--commands/move-tab.go4
-rw-r--r--commands/msg/archive.go7
-rw-r--r--commands/msg/copy.go6
-rw-r--r--commands/msg/delete.go4
-rw-r--r--commands/msg/envelope.go4
-rw-r--r--commands/msg/fold.go4
-rw-r--r--commands/msg/forward.go7
-rw-r--r--commands/msg/invite.go4
-rw-r--r--commands/msg/mark.go4
-rw-r--r--commands/msg/modify-labels.go6
-rw-r--r--commands/msg/move.go6
-rw-r--r--commands/msg/pipe.go4
-rw-r--r--commands/msg/read.go13
-rw-r--r--commands/msg/recall.go4
-rw-r--r--commands/msg/reply.go7
-rw-r--r--commands/msg/toggle-thread-context.go4
-rw-r--r--commands/msg/toggle-threads.go4
-rw-r--r--commands/msg/unsubscribe.go5
-rw-r--r--commands/msgview/close.go4
-rw-r--r--commands/msgview/next-part.go4
-rw-r--r--commands/msgview/next.go4
-rw-r--r--commands/msgview/open-link.go6
-rw-r--r--commands/msgview/open.go4
-rw-r--r--commands/msgview/save.go12
-rw-r--r--commands/msgview/toggle-headers.go4
-rw-r--r--commands/msgview/toggle-key-passthrough.go4
-rw-r--r--commands/new-account.go4
-rw-r--r--commands/next-tab.go4
-rw-r--r--commands/parser.go134
-rw-r--r--commands/parser_test.go258
-rw-r--r--commands/pin-tab.go4
-rw-r--r--commands/prompt.go56
-rw-r--r--commands/pwd.go4
-rw-r--r--commands/quit.go4
-rw-r--r--commands/send-keys.go4
-rw-r--r--commands/suspend.go4
-rw-r--r--commands/term.go4
-rw-r--r--commands/terminal/close.go4
-rw-r--r--commands/util.go19
-rw-r--r--commands/z.go7
-rw-r--r--main.go2
81 files changed, 197 insertions, 968 deletions
diff --git a/commands/account/cf.go b/commands/account/cf.go
index 59203a89..d73d4978 100644
--- a/commands/account/cf.go
+++ b/commands/account/cf.go
@@ -11,7 +11,7 @@ import (
var history map[string]string
type ChangeFolder struct {
- Folder string `opt:"..." metavar:"<folder>"`
+ Folder string `opt:"folder" complete:"CompleteFolder"`
}
func init() {
@@ -23,8 +23,8 @@ func (ChangeFolder) Aliases() []string {
return []string{"cf"}
}
-func (ChangeFolder) Complete(args []string) []string {
- return commands.GetFolders(args)
+func (*ChangeFolder) CompleteFolder(arg string) []string {
+ return commands.GetFolders(arg)
}
func (c ChangeFolder) Execute(args []string) error {
diff --git a/commands/account/check-mail.go b/commands/account/check-mail.go
index a8d80a66..d31b2648 100644
--- a/commands/account/check-mail.go
+++ b/commands/account/check-mail.go
@@ -16,10 +16,6 @@ func (CheckMail) Aliases() []string {
return []string{"check-mail"}
}
-func (CheckMail) Complete(args []string) []string {
- return nil
-}
-
func (CheckMail) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/clear.go b/commands/account/clear.go
index fd209d07..dec6bcd2 100644
--- a/commands/account/clear.go
+++ b/commands/account/clear.go
@@ -19,10 +19,6 @@ func (Clear) Aliases() []string {
return []string{"clear"}
}
-func (Clear) Complete(args []string) []string {
- return nil
-}
-
func (c Clear) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/compose.go b/commands/account/compose.go
index 81eb3de0..fe86a179 100644
--- a/commands/account/compose.go
+++ b/commands/account/compose.go
@@ -11,12 +11,13 @@ import (
"github.com/emersion/go-message/mail"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/config"
)
type Compose struct {
Headers string `opt:"-H" action:"ParseHeader"`
- Template string `opt:"-T"`
+ Template string `opt:"-T" complete:"CompleteTemplate"`
Edit bool `opt:"-e"`
NoEdit bool `opt:"-E"`
Body string `opt:"..." required:"false"`
@@ -37,12 +38,12 @@ func (c *Compose) ParseHeader(arg string) error {
return nil
}
-func (Compose) Aliases() []string {
- return []string{"compose"}
+func (*Compose) CompleteTemplate(arg string) []string {
+ return commands.GetTemplates(arg)
}
-func (Compose) Complete(args []string) []string {
- return nil
+func (Compose) Aliases() []string {
+ return []string{"compose"}
}
func (c Compose) Execute(args []string) error {
diff --git a/commands/account/connection.go b/commands/account/connection.go
index b9cd887b..d633c1ce 100644
--- a/commands/account/connection.go
+++ b/commands/account/connection.go
@@ -18,10 +18,6 @@ func (Connection) Aliases() []string {
return []string{"connect", "disconnect"}
}
-func (Connection) Complete(args []string) []string {
- return nil
-}
-
func (c Connection) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/expand-folder.go b/commands/account/expand-folder.go
index f26da70a..c264872a 100644
--- a/commands/account/expand-folder.go
+++ b/commands/account/expand-folder.go
@@ -16,10 +16,6 @@ func (ExpandCollapseFolder) Aliases() []string {
return []string{"expand-folder", "collapse-folder"}
}
-func (ExpandCollapseFolder) Complete(args []string) []string {
- return nil
-}
-
func (ExpandCollapseFolder) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/export-mbox.go b/commands/account/export-mbox.go
index 00e03ca6..14d9290e 100644
--- a/commands/account/export-mbox.go
+++ b/commands/account/export-mbox.go
@@ -17,7 +17,7 @@ import (
)
type ExportMbox struct {
- Filename string `opt:"filename"`
+ Filename string `opt:"filename" complete:"CompleteFilename"`
}
func init() {
@@ -28,8 +28,8 @@ func (ExportMbox) Aliases() []string {
return []string{"export-mbox"}
}
-func (ExportMbox) Complete(args []string) []string {
- return commands.CompletePath(filepath.Join(args...))
+func (*ExportMbox) CompleteFilename(arg string) []string {
+ return commands.CompletePath(arg)
}
func (e ExportMbox) Execute(args []string) error {
diff --git a/commands/account/import-mbox.go b/commands/account/import-mbox.go
index b3ad2a08..774dfa8f 100644
--- a/commands/account/import-mbox.go
+++ b/commands/account/import-mbox.go
@@ -6,7 +6,6 @@ import (
"fmt"
"io"
"os"
- "path/filepath"
"sync/atomic"
"time"
@@ -19,7 +18,7 @@ import (
)
type ImportMbox struct {
- Filename string `opt:"filename"`
+ Filename string `opt:"filename" complete:"CompleteFilename"`
}
func init() {
@@ -30,8 +29,8 @@ func (ImportMbox) Aliases() []string {
return []string{"import-mbox"}
}
-func (ImportMbox) Complete(args []string) []string {
- return commands.CompletePath(filepath.Join(args...))
+func (*ImportMbox) CompleteFilename(arg string) []string {
+ return commands.CompletePath(arg)
}
func (i ImportMbox) Execute(args []string) error {
diff --git a/commands/account/mkdir.go b/commands/account/mkdir.go
index 9beeb01a..3e546ace 100644
--- a/commands/account/mkdir.go
+++ b/commands/account/mkdir.go
@@ -2,15 +2,15 @@ package account
import (
"errors"
- "strings"
"time"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/worker/types"
)
type MakeDir struct {
- Folder string `opt:"..." metavar:"<folder>"`
+ Folder string `opt:"folder" complete:"CompleteFolder"`
}
func init() {
@@ -21,26 +21,15 @@ func (MakeDir) Aliases() []string {
return []string{"mkdir"}
}
-func (MakeDir) Complete(args []string) []string {
- if len(args) == 0 {
+func (*MakeDir) CompleteFolder(arg string) []string {
+ acct := app.SelectedAccount()
+ if acct == nil {
return nil
}
- name := strings.Join(args, " ")
-
- list := app.SelectedAccount().Directories().List()
- inboxes := make([]string, len(list))
- copy(inboxes, list)
-
- // remove inboxes that don't match and append the path separator to all
- // others
- for i := len(inboxes) - 1; i >= 0; i-- {
- if !strings.HasPrefix(inboxes[i], name) && name != "" {
- inboxes = append(inboxes[:i], inboxes[i+1:]...)
- continue
- }
- inboxes[i] += app.SelectedAccount().Worker().PathSeparator()
- }
- return inboxes
+ return commands.FilterList(
+ acct.Directories().List(), arg, "",
+ app.SelectedAccount().Worker().PathSeparator(),
+ app.SelectedAccountUiConfig().FuzzyComplete)
}
func (m MakeDir) Execute(args []string) error {
diff --git a/commands/account/next-folder.go b/commands/account/next-folder.go
index 1b5651c8..f44abdc1 100644
--- a/commands/account/next-folder.go
+++ b/commands/account/next-folder.go
@@ -18,10 +18,6 @@ func (NextPrevFolder) Aliases() []string {
return []string{"next-folder", "prev-folder"}
}
-func (NextPrevFolder) Complete(args []string) []string {
- return nil
-}
-
func (np NextPrevFolder) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/next-result.go b/commands/account/next-result.go
index e841899f..d624e559 100644
--- a/commands/account/next-result.go
+++ b/commands/account/next-result.go
@@ -17,10 +17,6 @@ func (NextPrevResult) Aliases() []string {
return []string{"next-result", "prev-result"}
}
-func (NextPrevResult) Complete(args []string) []string {
- return nil
-}
-
func (NextPrevResult) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/next.go b/commands/account/next.go
index 14679b1b..142f6151 100644
--- a/commands/account/next.go
+++ b/commands/account/next.go
@@ -35,10 +35,6 @@ func (NextPrevMsg) Aliases() []string {
return []string{"next", "next-message", "prev", "prev-message"}
}
-func (NextPrevMsg) Complete(args []string) []string {
- return nil
-}
-
func (np NextPrevMsg) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/recover.go b/commands/account/recover.go
index dae3d807..cba6e0cb 100644
--- a/commands/account/recover.go
+++ b/commands/account/recover.go
@@ -16,7 +16,7 @@ type Recover struct {
Force bool `opt:"-f"`
Edit bool `opt:"-e"`
NoEdit bool `opt:"-E"`
- File string `opt:"file"`
+ File string `opt:"file" complete:"CompleteFile"`
}
func init() {
@@ -31,7 +31,7 @@ func (Recover) Options() string {
return "feE"
}
-func (r Recover) Complete(args []string) []string {
+func (*Recover) CompleteFile(arg string) []string {
// file name of temp file is hard-coded in the NewComposer() function
files, err := filepath.Glob(
filepath.Join(os.TempDir(), "aerc-compose-*.eml"),
@@ -39,8 +39,7 @@ func (r Recover) Complete(args []string) []string {
if err != nil {
return nil
}
- return commands.CompletionFromList(files,
- commands.Operands(args, r.Options()))
+ return commands.CompletionFromList(files, arg)
}
func (r Recover) Execute(args []string) error {
diff --git a/commands/account/rmdir.go b/commands/account/rmdir.go
index 36bfc699..398f375f 100644
--- a/commands/account/rmdir.go
+++ b/commands/account/rmdir.go
@@ -20,10 +20,6 @@ func (RemoveDir) Aliases() []string {
return []string{"rmdir"}
}
-func (RemoveDir) Complete(args []string) []string {
- return nil
-}
-
func (r RemoveDir) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/search.go b/commands/account/search.go
index bb5617c0..ca1b9684 100644
--- a/commands/account/search.go
+++ b/commands/account/search.go
@@ -23,12 +23,12 @@ type SearchFilter struct {
Body bool `opt:"-b"`
All bool `opt:"-a"`
Headers textproto.MIMEHeader `opt:"-H" action:"ParseHeader" metavar:"<header>:<value>"`
- WithFlags models.Flags `opt:"-x" action:"ParseFlag"`
- WithoutFlags models.Flags `opt:"-X" action:"ParseNotFlag"`
- To []string `opt:"-t" action:"ParseTo"`
- From []string `opt:"-f" action:"ParseFrom"`
- Cc []string `opt:"-c" action:"ParseCc"`
- StartDate time.Time `opt:"-d" action:"ParseDate"`
+ WithFlags models.Flags `opt:"-x" action:"ParseFlag" complete:"CompleteFlag"`
+ WithoutFlags models.Flags `opt:"-X" action:"ParseNotFlag" complete:"CompleteFlag"`
+ To []string `opt:"-t" action:"ParseTo" complete:"CompleteAddress"`
+ From []string `opt:"-f" action:"ParseFrom" complete:"CompleteAddress"`
+ Cc []string `opt:"-c" action:"ParseCc" complete:"CompleteAddress"`
+ StartDate time.Time `opt:"-d" action:"ParseDate" complete:"CompleteDate"`
EndDate time.Time
Terms string `opt:"..." required:"false"`
}
@@ -37,33 +37,20 @@ func init() {
register(SearchFilter{})
}
-func (SearchFilter) Options() string {
- return "rubax:X:t:H:f:c:d:"
-}
-
func (SearchFilter) Aliases() []string {
return []string{"search", "filter"}
}
-func (s SearchFilter) CompleteOption(
- r rune,
- search string,
-) []string {
- var valid []string
- switch r {
- case 'x', 'X':
- valid = commands.GetFlagList()
- case 't', 'f', 'c':
- valid = commands.GetAddress(search)
- case 'd':
- valid = commands.GetDateList()
- default:
- }
- return commands.CompletionFromList(valid, []string{search})
+func (*SearchFilter) CompleteFlag(arg string) []string {
+ return commands.CompletionFromList(commands.GetFlagList(), arg)
}
-func (SearchFilter) Complete(args []string) []string {
- return nil
+func (*SearchFilter) CompleteAddress(arg string) []string {
+ return commands.CompletionFromList(commands.GetAddress(arg), arg)
+}
+
+func (*SearchFilter) CompleteDate(arg string) []string {
+ return commands.CompletionFromList(commands.GetDateList(), arg)
}
func (s *SearchFilter) ParseRead(arg string) error {
diff --git a/commands/account/select.go b/commands/account/select.go
index efd8f53b..884b4bce 100644
--- a/commands/account/select.go
+++ b/commands/account/select.go
@@ -18,10 +18,6 @@ func (SelectMessage) Aliases() []string {
return []string{"select", "select-message"}
}
-func (SelectMessage) Complete(args []string) []string {
- return nil
-}
-
func (s SelectMessage) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/sort.go b/commands/account/sort.go
index 2adfbf19..ccccab25 100644
--- a/commands/account/sort.go
+++ b/commands/account/sort.go
@@ -2,7 +2,6 @@ package account
import (
"errors"
- "strings"
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/commands"
@@ -13,6 +12,9 @@ import (
type Sort struct {
Unused struct{} `opt:"-"`
+ // these fields are only used for completion
+ Reverse bool `opt:"-r"`
+ Criteria []string `opt:"criteria" complete:"CompleteCriteria"`
}
func init() {
@@ -23,46 +25,20 @@ func (Sort) Aliases() []string {
return []string{"sort"}
}
-func (Sort) Complete(args []string) []string {
- supportedCriteria := []string{
- "arrival",
- "cc",
- "date",
- "from",
- "read",
- "size",
- "subject",
- "to",
- "flagged",
- }
- if len(args) == 0 {
- return supportedCriteria
- }
- last := args[len(args)-1]
- var completions []string
- currentPrefix := strings.Join(args, " ") + " "
- // if there is a completed criteria or option then suggest all again
- for _, criteria := range append(supportedCriteria, "-r") {
- if criteria == last {
- for _, criteria := range supportedCriteria {
- completions = append(completions, currentPrefix+criteria)
- }
- return completions
- }
- }
+var supportedCriteria = []string{
+ "arrival",
+ "cc",
+ "date",
+ "from",
+ "read",
+ "size",
+ "subject",
+ "to",
+ "flagged",
+}
- currentPrefix = strings.Join(args[:len(args)-1], " ")
- if len(args) > 1 {
- currentPrefix += " "
- }
- // last was beginning an option
- if last == "-" {
- return []string{currentPrefix + "-r"}
- }
- // the last item is not complete
- completions = commands.FilterList(supportedCriteria, last, currentPrefix,
- app.SelectedAccountUiConfig().FuzzyComplete)
- return completions
+func (*Sort) CompleteCriteria(arg string) []string {
+ return commands.CompletionFromList(supportedCriteria, arg)
}
func (Sort) Execute(args []string) error {
diff --git a/commands/account/split.go b/commands/account/split.go
index 4774297f..8690d99a 100644
--- a/commands/account/split.go
+++ b/commands/account/split.go
@@ -33,10 +33,6 @@ func (Split) Aliases() []string {
return []string{"split", "vsplit", "hsplit"}
}
-func (Split) Complete(args []string) []string {
- return nil
-}
-
func (s Split) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/account/view.go b/commands/account/view.go
index 701a7738..0cdc175d 100644
--- a/commands/account/view.go
+++ b/commands/account/view.go
@@ -19,10 +19,6 @@ func (ViewMessage) Aliases() []string {
return []string{"view-message", "view"}
}
-func (ViewMessage) Complete(args []string) []string {
- return nil
-}
-
func (v ViewMessage) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/cd.go b/commands/cd.go
index 6620e732..d2dc5310 100644
--- a/commands/cd.go
+++ b/commands/cd.go
@@ -12,7 +12,7 @@ import (
var previousDir string
type ChangeDirectory struct {
- Target string `opt:"directory" default:"~"`
+ Target string `opt:"directory" default:"~" complete:"CompleteTarget"`
}
func init() {
@@ -23,9 +23,8 @@ func (ChangeDirectory) Aliases() []string {
return []string{"cd"}
}
-func (ChangeDirectory) Complete(args []string) []string {
- path := strings.Join(args, " ")
- completions := CompletePath(path)
+func (*ChangeDirectory) CompleteTarget(arg string) []string {
+ completions := CompletePath(arg)
var dirs []string
for _, c := range completions {
diff --git a/commands/choose.go b/commands/choose.go
index 4e2007fd..19958dce 100644
--- a/commands/choose.go
+++ b/commands/choose.go
@@ -18,10 +18,6 @@ func (Choose) Aliases() []string {
return []string{"choose"}
}
-func (Choose) Complete(args []string) []string {
- return nil
-}
-
func (Choose) Execute(args []string) error {
if len(args) < 5 || len(args)%4 != 1 {
return chooseUsage(args[0])
diff --git a/commands/commands.go b/commands/commands.go
index d76194af..9c193018 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -3,12 +3,13 @@ package commands
import (
"bytes"
"errors"
+ "path"
"reflect"
+ "sort"
"strings"
"unicode"
"git.sr.ht/~rjarry/go-opt"
- "github.com/google/shlex"
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/config"
@@ -21,17 +22,6 @@ import (
type Command interface {
Aliases() []string
Execute([]string) error
- Complete([]string) []string
-}
-
-type OptionsProvider interface {
- Command
- Options() string
-}
-
-type OptionCompleter interface {
- OptionsProvider
- CompleteOption(rune, string) []string
}
type Commands map[string]Command
@@ -174,6 +164,7 @@ func GetTemplateCompletion(
templates.Terms(),
strings.TrimSpace(search),
"",
+ "",
app.SelectedAccountUiConfig().FuzzyComplete,
)
return options, prefix + padding, true
@@ -194,117 +185,62 @@ func GetTemplateCompletion(
func GetCompletions(
cmd Command, args *opt.Args,
) (options []string, prefix string) {
- // complete options
- var spec string
- if provider, ok := cmd.(OptionsProvider); ok {
- spec = provider.Options()
- }
-
- parser, err := newParser(args.String(), spec, strings.HasSuffix(args.String(), " "))
- if err != nil {
- log.Debugf("completion parser failed: %v", err)
- return
- }
-
- switch parser.kind {
- case SHORT_OPTION:
- for _, r := range strings.ReplaceAll(spec, ":", "") {
- if strings.ContainsRune(parser.flag, r) {
- continue
- }
- option := string(r)
- if strings.Contains(spec, option+":") {
- option += " "
- }
- options = append(options, option)
- }
- prefix = args.String()
- case OPTION_ARGUMENT:
- cmpl, ok := cmd.(OptionCompleter)
- if !ok {
- return
- }
- stem := args.String()
- if parser.arg != "" {
- stem = strings.TrimSuffix(stem, parser.arg)
- }
- pad := ""
- if !strings.HasSuffix(stem, " ") {
- pad += " "
- }
- s := parser.flag
- r := rune(s[len(s)-1])
- for _, option := range cmpl.CompleteOption(r, parser.arg) {
- options = append(options, pad+escape(option)+" ")
- }
- prefix = stem
- case OPERAND:
- clone := args.Clone()
- clone.Cut(clone.Count() - parser.optind)
- args.Shift(1)
- for _, option := range cmd.Complete(args.Args()) {
- if strings.Contains(option, " ") {
- option = escape(option)
- }
- options = append(options, " "+option)
- }
- prefix = clone.String()
- }
-
- return
+ // copy zeroed struct
+ tmp := reflect.New(reflect.TypeOf(cmd)).Interface().(Command)
+ spec := opt.NewCmdSpec(args.Arg(0), tmp)
+ return spec.GetCompletions(args)
}
-func GetFolders(args []string) []string {
+func GetFolders(arg string) []string {
acct := app.SelectedAccount()
if acct == nil {
return make([]string, 0)
}
- if len(args) == 0 {
- return acct.Directories().List()
- }
- return FilterList(acct.Directories().List(), args[0], "", acct.UiConfig().FuzzyComplete)
+ return CompletionFromList(acct.Directories().List(), arg)
}
-// CompletionFromList provides a convenience wrapper for commands to use in the
-// Complete function. It simply matches the items provided in valid
-func CompletionFromList(valid []string, args []string) []string {
- if len(args) == 0 {
- return valid
+func GetTemplates(arg string) []string {
+ templates := make(map[string]bool)
+ for _, dir := range config.Templates.TemplateDirs {
+ for _, f := range listDir(dir, false) {
+ if !isDir(path.Join(dir, f)) {
+ templates[f] = true
+ }
+ }
+ }
+ names := make([]string, len(templates))
+ for n := range templates {
+ names = append(names, n)
}
- return FilterList(valid, args[0], "", app.SelectedAccountUiConfig().FuzzyComplete)
+ sort.Strings(names)
+ return CompletionFromList(names, arg)
}
-func GetLabels(args []string) []string {
+// CompletionFromList provides a convenience wrapper for commands to use in a
+// complete callback. It simply matches the items provided in valid
+func CompletionFromList(valid []string, arg string) []string {
+ return FilterList(valid, arg, "", "", app.SelectedAccountUiConfig().FuzzyComplete)
+}
+
+func GetLabels(arg string) []string {
acct := app.SelectedAccount()
if acct == nil {
return make([]string, 0)
}
- if len(args) == 0 {
- return acct.Labels()
- }
-
- // + and - are used to denote tag addition / removal and need to be striped
- // only the last tag should be completed, so that multiple labels can be
- // selected
- last := args[len(args)-1]
- others := strings.Join(args[:len(args)-1], " ")
var prefix string
- switch last[0] {
- case '+':
- prefix = "+"
- case '-':
- prefix = "-"
- default:
- prefix = ""
- }
- trimmed := strings.TrimLeft(last, "+-")
-
- var prev string
- if len(others) > 0 {
- prev = others + " "
+ if arg != "" {
+ // + and - are used to denote tag addition / removal and need to
+ // be striped only the last tag should be completed, so that
+ // multiple labels can be selected
+ switch arg[0] {
+ case '+':
+ prefix = "+"
+ case '-':
+ prefix = "-"
+ }
+ arg = strings.TrimLeft(arg, "+-")
}
- out := FilterList(acct.Labels(), trimmed, prev+prefix, acct.UiConfig().FuzzyComplete)
- return out
+ return FilterList(acct.Labels(), arg, prefix, " ", acct.UiConfig().FuzzyComplete)
}
// hasCaseSmartPrefix checks whether s starts with prefix, using a case
@@ -324,19 +260,3 @@ func hasUpper(s string) bool {
}
return false
}
-
-// splitCmd splits the command into arguments
-func splitCmd(cmd string) ([]string, error) {
- args, err := shlex.Split(cmd)
- if err != nil {
- return nil, err
- }
- return args, nil
-}
-
-func escape(s string) string {
- if strings.Contains(s, " ") {
- return strings.ReplaceAll(s, " ", "\\ ")
- }
- return s
-}
diff --git a/commands/compose/abort.go b/commands/compose/abort.go
index 4bacb9c3..cde43f46 100644
--- a/commands/compose/abort.go
+++ b/commands/compose/abort.go
@@ -14,10 +14,6 @@ func (Abort) Aliases() []string {
return []string{"abort"}
}
-func (Abort) Complete(args []string) []string {
- return nil
-}
-
func (Abort) Execute(args []string) error {
composer, _ := app.SelectedTabContent().(*app.Composer)
app.RemoveTab(composer, true)
diff --git a/commands/compose/attach-key.go b/commands/compose/attach-key.go
index e81a6a5a..6ba5eae0 100644
--- a/commands/compose/attach-key.go
+++ b/commands/compose/attach-key.go
@@ -14,10 +14,6 @@ func (AttachKey) Aliases() []string {
return []string{"attach-key"}
}
-func (AttachKey) Complete(args []string) []string {
- return nil
-}
-
func (AttachKey) Execute(args []string) error {
composer, _ := app.SelectedTabContent().(*app.Composer)
return composer.SetAttachKey(!composer.AttachKey())
diff --git a/commands/compose/attach.go b/commands/compose/attach.go
index 3acf28c2..49d63bb8 100644
--- a/commands/compose/attach.go
+++ b/commands/compose/attach.go
@@ -23,7 +23,8 @@ import (
type Attach struct {
Menu bool `opt:"-m"`
Name string `opt:"-r"`
- Path string `opt:"..." metavar:"<path>" required:"false"`
+ Path string `opt:"path" required:"false" complete:"CompletePath"`
+ Args string `opt:"..." required:"false"`
}
func init() {
@@ -34,9 +35,8 @@ func (Attach) Aliases() []string {
return []string{"attach"}
}
-func (Attach) Complete(args []string) []string {
- path := strings.Join(args, " ")
- return commands.CompletePath(path)
+func (*Attach) CompletePath(arg string) []string {
+ return commands.CompletePath(arg)
}
func (a Attach) Execute(args []string) error {
@@ -52,6 +52,9 @@ func (a Attach) Execute(args []string) error {
}
return a.readCommand()
default:
+ if a.Args != "" {
+ return errors.New("only a single path is supported")
+ }
return a.addPath(a.Path)
}
}
@@ -186,7 +189,7 @@ func (a Attach) openMenu() error {
}
func (a Attach) readCommand() error {
- cmd := exec.Command("sh", "-c", a.Path)
+ cmd := exec.Command("sh", "-c", a.Path+" "+a.Args)
data, err := cmd.Output()
if err != nil {
diff --git a/commands/compose/cc-bcc.go b/commands/compose/cc-bcc.go
index aeb6af97..dd5e9614 100644
--- a/commands/compose/cc-bcc.go
+++ b/commands/compose/cc-bcc.go
@@ -16,10 +16,6 @@ func (CC) Aliases() []string {
return []string{"cc", "bcc"}
}
-func (CC) Complete(args []string) []string {
- return nil
-}
-
func (c CC) Execute(args []string) error {
composer, _ := app.SelectedTabContent().(*app.Composer)
diff --git a/commands/compose/detach.go b/commands/compose/detach.go
index a2996516..91cf2a58 100644
--- a/commands/compose/detach.go
+++ b/commands/compose/detach.go
@@ -4,10 +4,11 @@ import (
"fmt"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
)
type Detach struct {
- Path string `opt:"path" required:"false"`
+ Path string `opt:"path" required:"false" complete:"CompletePath"`
}
func init() {
@@ -18,9 +19,9 @@ func (Detach) Aliases() []string {
return []string{"detach"}
}
-func (Detach) Complete(args []string) []string {
+func (*Detach) CompletePath(arg string) []string {
composer, _ := app.SelectedTabContent().(*app.Composer)
- return composer.GetAttachments()
+ return commands.CompletionFromList(composer.GetAttachments(), arg)
}
func (d Detach) Execute(args []string) error {
diff --git a/commands/compose/edit.go b/commands/compose/edit.go
index 80f4e6f4..1929f45a 100644
--- a/commands/compose/edit.go
+++ b/commands/compose/edit.go
@@ -20,10 +20,6 @@ func (Edit) Aliases() []string {
return []string{"edit"}
}
-func (Edit) Complete(args []string) []string {
- return nil
-}
-
func (e Edit) Execute(args []string) error {
composer, ok := app.SelectedTabContent().(*app.Composer)
if !ok {
diff --git a/commands/compose/encrypt.go b/commands/compose/encrypt.go
index 3121dff0..ee902f6e 100644
--- a/commands/compose/encrypt.go
+++ b/commands/compose/encrypt.go
@@ -14,10 +14,6 @@ func (Encrypt) Aliases() []string {
return []string{"encrypt"}
}
-func (Encrypt) Complete(args []string) []string {
- return nil
-}
-
func (Encrypt) Execute(args []string) error {
composer, _ := app.SelectedTabContent().(*app.Composer)
composer.SetEncrypt(!composer.Encrypt())
diff --git a/commands/compose/header.go b/commands/compose/header.go
index aaa14e43..afc27e92 100644
--- a/commands/compose/header.go
+++ b/commands/compose/header.go
@@ -11,7 +11,7 @@ import (
type Header struct {
Force bool `opt:"-f"`
Remove bool `opt:"-d"`
- Name string `opt:"name"`
+ Name string `opt:"name" complete:"CompleteHeaders"`
Value string `opt:"..." required:"false"`
}
@@ -37,8 +37,8 @@ func (Header) Options() string {
return "fd"
}
-func (Header) Complete(args []string) []string {
- return commands.CompletionFromList(headers, args)
+func (*Header) CompleteHeaders(arg string) []string {
+ return commands.CompletionFromList(headers, arg)
}
func (h Header) Execute(args []string) error {
diff --git a/commands/compose/multipart.go b/commands/compose/multipart.go
index 96941062..5b701342 100644
--- a/commands/compose/multipart.go
+++ b/commands/compose/multipart.go
@@ -11,7 +11,7 @@ import (
type Multipart struct {
Remove bool `opt:"-d"`
- Mime string `opt:"mime" metavar:"<mime/type>"`
+ Mime string `opt:"mime" metavar:"<mime/type>" complete:"CompleteMime"`
}
func init() {
@@ -22,13 +22,12 @@ func (Multipart) Aliases() []string {
return []string{"multipart"}
}
-func (Multipart) Complete(args []string) []string {
+func (*Multipart) CompleteMime(arg string) []string {
var completions []string
- completions = append(completions, "-d")
for mime := range config.Converters {
completions = append(completions, mime)
}
- return commands.CompletionFromList(completions, args)
+ return commands.CompletionFromList(completions, arg)
}
func (m Multipart) Execute(args []string) error {
diff --git a/commands/compose/next-field.go b/commands/compose/next-field.go
index 88fbb03d..396c1ce1 100644
--- a/commands/compose/next-field.go
+++ b/commands/compose/next-field.go
@@ -14,10 +14,6 @@ func (NextPrevField) Aliases() []string {
return []string{"next-field", "prev-field"}
}
-func (NextPrevField) Complete(args []string) []string {
- return nil
-}
-
func (NextPrevField) Execute(args []string) error {
composer, _ := app.SelectedTabContent().(*app.Composer)
if args[0] == "prev-field" {
diff --git a/commands/compose/postpone.go b/commands/compose/postpone.go
index 767e759c..3ce9cc84 100644
--- a/commands/compose/postpone.go
+++ b/commands/compose/postpone.go
@@ -14,7 +14,7 @@ import (
)
type Postpone struct {
- Folder string `opt:"-t"`
+ Folder string `opt:"-t" complete:"CompleteFolder"`
}
func init() {
@@ -25,20 +25,8 @@ func (Postpone) Aliases() []string {
return []string{"postpone"}
}
-func (Postpone) Options() string {
- return "t:"
-}
-
-func (Postpone) CompleteOption(r rune, arg string) []string {
- var valid []string
- if r == 't' {
- valid = commands.GetFolders([]string{arg})
- }
- return commands.CompletionFromList(valid, []string{arg})
-}
-
-func (Postpone) Complete(args []string) []string {
- return nil
+func (*Postpone) CompleteFolder(arg string) []string {
+ return commands.GetFolders(arg)
}
func (p Postpone) Execute(args []string) error {
diff --git a/commands/compose/send.go b/commands/compose/send.go
index e3672471..6f905628 100644
--- a/commands/compose/send.go
+++ b/commands/compose/send.go
@@ -28,8 +28,8 @@ import (
)
type Send struct {
- Archive string `opt:"-a" action:"ParseArchive" metavar:"flat|year|month"`
- CopyTo string `opt:"-t"`
+ Archive string `opt:"-a" action:"ParseArchive" metavar:"flat|year|month" complete:"CompleteArchive"`
+ CopyTo string `opt:"-t" complete:"CompleteFolders"`
}
func init() {
@@ -40,19 +40,12 @@ func (Send) Aliases() []string {
return []string{"send"}
}
-func (Send) Options() string {
- return "a:t:"
+func (*Send) CompleteArchive(arg string) []string {
+ return commands.CompletionFromList(msg.ARCHIVE_TYPES, arg)
}
-func (s Send) CompleteOption(r rune, term string) []string {
- if r == 't' {
- return commands.GetFolders([]string{term})
- }
- return nil
-}
-
-func (Send) Complete(args []string) []string {
- return nil
+func (*Send) CompleteFolders(arg string) []string {
+ return commands.GetFolders(arg)
}
func (s *Send) ParseArchive(arg string) error {
diff --git a/commands/compose/sign.go b/commands/compose/sign.go
index e30fca05..faeceed4 100644
--- a/commands/compose/sign.go
+++ b/commands/compose/sign.go
@@ -16,10 +16,6 @@ func (Sign) Aliases() []string {
return []string{"sign"}
}
-func (Sign) Complete(args []string) []string {
- return nil
-}
-
func (Sign) Execute(args []string) error {
composer, _ := app.SelectedTabContent().(*app.Composer)
diff --git a/commands/compose/switch.go b/commands/compose/switch.go
index 0c027a41..637099b5 100644
--- a/commands/compose/switch.go
+++ b/commands/compose/switch.go
@@ -4,6 +4,7 @@ import (
"errors"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
)
type AccountSwitcher interface {
@@ -13,7 +14,7 @@ type AccountSwitcher interface {
type SwitchAccount struct {
Next bool `opt:"-n"`
Prev bool `opt:"-p"`
- Account string `opt:"..." metavar:"<account>" required:"false"`
+ Account string `opt:"account" required:"false" complete:"CompleteAccount"`
}
func init() {
@@ -24,8 +25,8 @@ func (SwitchAccount) Aliases() []string {
return []string{"switch-account"}
}
-func (SwitchAccount) Complete(args []string) []string {
- return app.AccountNames()
+func (*SwitchAccount) CompleteAccount(arg string) []string {
+ return commands.CompletionFromList(app.AccountNames(), arg)
}
func (s SwitchAccount) Execute(args []string) error {
diff --git a/commands/ct.go b/commands/ct.go
index 8a6bb063..2d057b4f 100644
--- a/commands/ct.go
+++ b/commands/ct.go
@@ -9,7 +9,7 @@ import (
)
type ChangeTab struct {
- Tab string `opt:"tab"`
+ Tab string `opt:"tab" complete:"CompleteTab"`
}
func init() {
@@ -20,12 +20,8 @@ func (ChangeTab) Aliases() []string {
return []string{"ct", "change-tab"}
}
-func (ChangeTab) Complete(args []string) []string {
- if len(args) == 0 {
- return app.TabNames()
- }
- joinedArgs := strings.Join(args, " ")
- return FilterList(app.TabNames(), joinedArgs, "", app.SelectedAccountUiConfig().FuzzyComplete)
+func (*ChangeTab) CompleteTab(arg string) []string {
+ return CompletionFromList(app.TabNames(), arg)
}
func (c ChangeTab) Execute(args []string) error {
diff --git a/commands/eml.go b/commands/eml.go
index adacd05b..fe735243 100644
--- a/commands/eml.go
+++ b/commands/eml.go
@@ -5,14 +5,13 @@ import (
"fmt"
"io"
"os"
- "strings"
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/lib"
)
type Eml struct {
- Path string `opt:"path" required:"false"`
+ Path string `opt:"path" required:"false" complete:"CompletePath"`
}
func init() {
@@ -23,8 +22,8 @@ func (Eml) Aliases() []string {
return []string{"eml", "preview"}
}
-func (Eml) Complete(args []string) []string {
- return CompletePath(strings.Join(args, " "))
+func (*Eml) CompletePath(arg string) []string {
+ return CompletePath(arg)
}
func (e Eml) Execute(args []string) error {
diff --git a/commands/exec.go b/commands/exec.go
index 4c2f3d1b..2a3ed5f5 100644
--- a/commands/exec.go
+++ b/commands/exec.go
@@ -22,10 +22,6 @@ func (ExecCmd) Aliases() []string {
return []string{"exec"}
}
-func (ExecCmd) Complete(args []string) []string {
- return nil
-}
-
func (e ExecCmd) Execute(args []string) error {
cmd := exec.Command(e.Args[0], e.Args[1:]...)
env := os.Environ()
diff --git a/commands/help.go b/commands/help.go
index b2bcdf7c..07332303 100644
--- a/commands/help.go
+++ b/commands/help.go
@@ -7,7 +7,7 @@ import (
)
type Help struct {
- Topic string `opt:"topic" action:"ParseTopic" default:"aerc"`
+ Topic string `opt:"topic" action:"ParseTopic" default:"aerc" complete:"CompleteTopic"`
}
var pages = []string{
@@ -35,8 +35,8 @@ func (Help) Aliases() []string {
return []string{"help"}
}
-func (Help) Complete(args []string) []string {
- return CompletionFromList(pages, args)
+func (*Help) CompleteTopic(arg string) []string {
+ return CompletionFromList(pages, arg)
}
func (h *Help) ParseTopic(arg string) error {
diff --git a/commands/move-tab.go b/commands/move-tab.go
index 23580f2d..7ebe269c 100644
--- a/commands/move-tab.go
+++ b/commands/move-tab.go
@@ -32,10 +32,6 @@ func (MoveTab) Aliases() []string {
return []string{"move-tab"}
}
-func (MoveTab) Complete(args []string) []string {
- return nil
-}
-
func (m MoveTab) Execute(args []string) error {
app.MoveTab(m.Index, m.Relative)
return nil
diff --git a/commands/msg/archive.go b/commands/msg/archive.go
index f4d6e3be..34cba8b8 100644
--- a/commands/msg/archive.go
+++ b/commands/msg/archive.go
@@ -21,7 +21,7 @@ const (
var ARCHIVE_TYPES = []string{ARCHIVE_FLAT, ARCHIVE_YEAR, ARCHIVE_MONTH}
type Archive struct {
- Type string `opt:"type" action:"ParseArchiveType" metavar:"flat|year|month"`
+ Type string `opt:"type" action:"ParseArchiveType" metavar:"flat|year|month" complete:"CompleteType"`
}
func (a *Archive) ParseArchiveType(arg string) error {
@@ -42,9 +42,8 @@ func (Archive) Aliases() []string {
return []string{"archive"}
}
-func (Archive) Complete(args []string) []string {
- valid := []string{"flat", "year", "month"}
- return commands.CompletionFromList(valid, args)
+func (*Archive) CompleteType(arg string) []string {
+ return commands.CompletionFromList(ARCHIVE_TYPES, arg)
}
func (a Archive) Execute(args []string) error {
diff --git a/commands/msg/copy.go b/commands/msg/copy.go
index 4109ef99..77c9ade1 100644
--- a/commands/msg/copy.go
+++ b/commands/msg/copy.go
@@ -10,7 +10,7 @@ import (
type Copy struct {
CreateFolders bool `opt:"-p"`
- Folder string `opt:"..." metavar:"<folder>"`
+ Folder string `opt:"folder" complete:"CompleteFolder"`
}
func init() {
@@ -21,8 +21,8 @@ func (Copy) Aliases() []string {
return []string{"cp", "copy"}
}
-func (Copy) Complete(args []string) []string {
- return commands.GetFolders(args)
+func (*Copy) CompleteFolder(arg string) []string {
+ return commands.GetFolders(arg)
}
func (c Copy) Execute(args []string) error {
diff --git a/commands/msg/delete.go b/commands/msg/delete.go
index 49463abc..1a1a130c 100644
--- a/commands/msg/delete.go
+++ b/commands/msg/delete.go
@@ -21,10 +21,6 @@ func (Delete) Aliases() []string {
return []string{"delete", "delete-message"}
}
-func (Delete) Complete(args []string) []string {
- return nil
-}
-
func (Delete) Execute(args []string) error {
h := newHelper()
store, err := h.store()
diff --git a/commands/msg/envelope.go b/commands/msg/envelope.go
index 6da82a1e..e104648d 100644
--- a/commands/msg/envelope.go
+++ b/commands/msg/envelope.go
@@ -25,10 +25,6 @@ func (Envelope) Aliases() []string {
return []string{"envelope"}
}
-func (Envelope) Complete(args []string) []string {
- return nil
-}
-
func (e Envelope) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/msg/fold.go b/commands/msg/fold.go
index 0621c8c3..06e933d7 100644
--- a/commands/msg/fold.go
+++ b/commands/msg/fold.go
@@ -17,10 +17,6 @@ func (Fold) Aliases() []string {
return []string{"fold", "unfold"}
}
-func (Fold) Complete(args []string) []string {
- return nil
-}
-
func (Fold) Execute(args []string) error {
h := newHelper()
store, err := h.store()
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index e6e386a9..4147e8c7 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -13,6 +13,7 @@ import (
"sync"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/lib/format"
@@ -27,7 +28,7 @@ type forward struct {
AttachFull bool `opt:"-F"`
Edit bool `opt:"-e"`
NoEdit bool `opt:"-E"`
- Template string `opt:"-T"`
+ Template string `opt:"-T" complete:"CompleteTemplate"`
To []string `opt:"..." required:"false"`
}
@@ -39,8 +40,8 @@ func (forward) Aliases() []string {
return []string{"forward"}
}
-func (forward) Complete(args []string) []string {
- return nil
+func (*forward) CompleteTemplate(arg string) []string {
+ return commands.GetTemplates(arg)
}
func (f forward) Execute(args []string) error {
diff --git a/commands/msg/invite.go b/commands/msg/invite.go
index 5b8558b0..12bea93e 100644
--- a/commands/msg/invite.go
+++ b/commands/msg/invite.go
@@ -28,10 +28,6 @@ func (invite) Aliases() []string {
return []string{"accept", "accept-tentative", "decline"}
}
-func (invite) Complete(args []string) []string {
- return nil
-}
-
func (i invite) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
diff --git a/commands/msg/mark.go b/commands/msg/mark.go
index c2c21cf2..9547548f 100644
--- a/commands/msg/mark.go
+++ b/commands/msg/mark.go
@@ -20,10 +20,6 @@ func (Mark) Aliases() []string {
return []string{"mark", "unmark", "remark"}
}
-func (Mark) Complete(args []string) []string {
- return nil
-}
-
func (m Mark) Execute(args []string) error {
h := newHelper()
OnSelectedMessage := func(fn func(uint32)) error {
diff --git a/commands/msg/modify-labels.go b/commands/msg/modify-labels.go
index 6fdbeac4..8cc72f2a 100644
--- a/commands/msg/modify-labels.go
+++ b/commands/msg/modify-labels.go
@@ -9,7 +9,7 @@ import (
)
type ModifyLabels struct {
- Labels []string `opt:"..." metavar:"[+-]<label>"`
+ Labels []string `opt:"..." metavar:"[+-]<label>" complete:"CompleteLabels"`
}
func init() {
@@ -20,8 +20,8 @@ func (ModifyLabels) Aliases() []string {
return []string{"modify-labels", "tag"}
}
-func (ModifyLabels) Complete(args []string) []string {
- return commands.GetLabels(args)
+func (*ModifyLabels) CompleteLabels(arg string) []string {
+ return commands.GetLabels(arg)
}
func (m ModifyLabels) Execute(args []string) error {
diff --git a/commands/msg/move.go b/commands/msg/move.go
index 1dd68d35..06c7fc15 100644
--- a/commands/msg/move.go
+++ b/commands/msg/move.go
@@ -14,7 +14,7 @@ import (
type Move struct {
CreateFolders bool `opt:"-p"`
- Folder string `opt:"..." metavar:"<folder>"`
+ Folder string `opt:"folder" complete:"CompleteFolder"`
}
func init() {
@@ -25,8 +25,8 @@ func (Move) Aliases() []string {
return []string{"mv", "move"}
}
-func (Move) Complete(args []string) []string {
- return commands.GetFolders(args)
+func (*Move) CompleteFolder(arg string) []string {
+ return commands.GetFolders(arg)
}
func (m Move) Execute(args []string) error {
diff --git a/commands/msg/pipe.go b/commands/msg/pipe.go
index b984752b..b32d7159 100644
--- a/commands/msg/pipe.go
+++ b/commands/msg/pipe.go
@@ -32,10 +32,6 @@ func (Pipe) Aliases() []string {
return []string{"pipe"}
}
-func (Pipe) Complete(args []string) []string {
- return nil
-}
-
func (p Pipe) Execute(args []string) error {
if p.Full && p.Part {
return errors.New("-m and -p are mutually exclusive")
diff --git a/commands/msg/read.go b/commands/msg/read.go
index e55ed00e..72159a53 100644
--- a/commands/msg/read.go
+++ b/commands/msg/read.go
@@ -6,6 +6,7 @@ import (
"time"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
@@ -13,7 +14,7 @@ import (
type FlagMsg struct {
Toggle bool `opt:"-t"`
Answered bool `opt:"-a" aliases:"flag,unflag"`
- Flag models.Flags `opt:"-x" aliases:"flag,unflag" action:"ParseFlag"`
+ Flag models.Flags `opt:"-x" aliases:"flag,unflag" action:"ParseFlag" complete:"CompleteFlag"`
FlagName string
}
@@ -25,10 +26,6 @@ func (FlagMsg) Aliases() []string {
return []string{"flag", "unflag", "read", "unread"}
}
-func (FlagMsg) Complete(args []string) []string {
- return nil
-}
-
func (f *FlagMsg) ParseFlag(arg string) error {
switch strings.ToLower(arg) {
case "seen":
@@ -46,6 +43,12 @@ func (f *FlagMsg) ParseFlag(arg string) error {
return nil
}
+var validFlags = []string{"seen", "answered", "flagged"}
+
+func (*FlagMsg) CompleteFlag(arg string) []string {
+ return commands.CompletionFromList(validFlags, arg)
+}
+
// If this was called as 'flag' or 'unflag', without the toggle (-t)
// option, then it will flag the corresponding messages with the given
// flag. If the toggle option was given, it will individually toggle
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index 3b78a763..cea02ddb 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -31,10 +31,6 @@ func (Recall) Aliases() []string {
return []string{"recall"}
}
-func (Recall) Complete(args []string) []string {
- return nil
-}
-
func (r Recall) Execute(args []string) error {
editHeaders := (config.Compose.EditHeaders || r.Edit) && !r.NoEdit
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index 2ab9e9f8..ebf20317 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -10,6 +10,7 @@ import (
"time"
"git.sr.ht/~rjarry/aerc/app"
+ "git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/commands/account"
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
@@ -26,7 +27,7 @@ type reply struct {
All bool `opt:"-a"`
Close bool `opt:"-c"`
Quote bool `opt:"-q"`
- Template string `opt:"-T"`
+ Template string `opt:"-T" complete:"CompleteTemplate"`
Edit bool `opt:"-e"`
NoEdit bool `opt:"-E"`
}
@@ -39,8 +40,8 @@ func (reply) Aliases() []string {
return []string{"reply"}
}
-func (reply) Complete(args []string) []string {
- return nil
+func (*reply) CompleteTemplate(arg string) []string {
+ return commands.GetTemplates(arg)
}
func (r reply) Execute(args []string) error {
diff --git a/commands/msg/toggle-thread-context.go b/commands/msg/toggle-thread-context.go
index 0ef6778f..4b87eaa8 100644
--- a/commands/msg/toggle-thread-context.go
+++ b/commands/msg/toggle-thread-context.go
@@ -14,10 +14,6 @@ func (ToggleThreadContext) Aliases() []string {
return []string{"toggle-thread-context"}
}
-func (ToggleThreadContext) Complete(args []string) []string {
- return nil
-}
-
func (ToggleThreadContext) Execute(args []string) error {
h := newHelper()
store, err := h.store()
diff --git a/commands/msg/toggle-threads.go b/commands/msg/toggle-threads.go
index 88cc763f..d2933bba 100644
--- a/commands/msg/toggle-threads.go
+++ b/commands/msg/toggle-threads.go
@@ -15,10 +15,6 @@ func (ToggleThreads) Aliases() []string {
return []string{"toggle-threads"}
}
-func (ToggleThreads) Complete(args []string) []string {
- return nil
-}
-
func (ToggleThreads) Execute(args []string) error {
h := newHelper()
acct, err := h.account()
diff --git a/commands/msg/unsubscribe.go b/commands/msg/unsubscribe.go
index 6fef16f5..c6a0b23f 100644
--- a/commands/msg/unsubscribe.go
+++ b/commands/msg/unsubscribe.go
@@ -31,11 +31,6 @@ func (Unsubscribe) Aliases() []string {
return []string{"unsubscribe"}
}
-// Complete returns a list of completions
-func (Unsubscribe) Complete(args []string) []string {
- return nil
-}
-
// Execute runs the Unsubscribe command
func (u Unsubscribe) Execute(args []string) error {
editHeaders := (config.Compose.EditHeaders || u.Edit) && !u.NoEdit
diff --git a/commands/msgview/close.go b/commands/msgview/close.go
index 32702da9..95960ef3 100644
--- a/commands/msgview/close.go
+++ b/commands/msgview/close.go
@@ -14,10 +14,6 @@ func (Close) Aliases() []string {
return []string{"close"}
}
-func (Close) Complete(args []string) []string {
- return nil
-}
-
func (Close) Execute(args []string) error {
mv, _ := app.SelectedTabContent().(*app.MessageViewer)
app.RemoveTab(mv, true)
diff --git a/commands/msgview/next-part.go b/commands/msgview/next-part.go
index 9b1b1fcc..77eb008d 100644
--- a/commands/msgview/next-part.go
+++ b/commands/msgview/next-part.go
@@ -16,10 +16,6 @@ func (NextPrevPart) Aliases() []string {
return []string{"next-part", "prev-part"}
}
-func (NextPrevPart) Complete(args []string) []string {
- return nil
-}
-
func (np NextPrevPart) Execute(args []string) error {
mv, _ := app.SelectedTabContent().(*app.MessageViewer)
for n := 0; n < np.Offset; n++ {
diff --git a/commands/msgview/next.go b/commands/msgview/next.go
index d8f046f8..c953cd5d 100644
--- a/commands/msgview/next.go
+++ b/commands/msgview/next.go
@@ -39,10 +39,6 @@ func (NextPrevMsg) Aliases() []string {
return []string{"next", "next-message", "prev", "prev-message"}
}
-func (NextPrevMsg) Complete(args []string) []string {
- return nil
-}
-
func (np NextPrevMsg) Execute(args []string) error {
cmd := account.NextPrevMsg{Amount: np.Amount, Percent: np.Percent}
err := cmd.Execute(args)
diff --git a/commands/msgview/open-link.go b/commands/msgview/open-link.go
index 55aee08c..eceb4232 100644
--- a/commands/msgview/open-link.go
+++ b/commands/msgview/open-link.go
@@ -11,7 +11,7 @@ import (
)
type OpenLink struct {
- Url *url.URL `opt:"url" action:"ParseUrl"`
+ Url *url.URL `opt:"url" action:"ParseUrl" complete:"CompleteUrl"`
Cmd string `opt:"..." required:"false"`
}
@@ -23,11 +23,11 @@ func (OpenLink) Aliases() []string {
return []string{"open-link"}
}
-func (OpenLink) Complete(args []string) []string {
+func (*OpenLink) CompleteUrl(arg string) []string {
mv := app.SelectedTabContent().(*app.MessageViewer)
if mv != nil {
if p := mv.SelectedMessagePart(); p != nil {
- return commands.CompletionFromList(p.Links, args)
+ return commands.CompletionFromList(p.Links, arg)
}
}
return nil
diff --git a/commands/msgview/open.go b/commands/msgview/open.go
index 0d6db5b0..a8443404 100644
--- a/commands/msgview/open.go
+++ b/commands/msgview/open.go
@@ -29,10 +29,6 @@ func (Open) Aliases() []string {
return []string{"open"}
}
-func (Open) Complete(args []string) []string {
- return nil
-}
-
func (o Open) Execute(args []string) error {
mv := app.SelectedTabContent().(*app.MessageViewer)
if mv == nil {
diff --git a/commands/msgview/save.go b/commands/msgview/save.go
index ea7599d3..9f2b71fc 100644
--- a/commands/msgview/save.go
+++ b/commands/msgview/save.go
@@ -22,7 +22,7 @@ type Save struct {
CreateDirs bool `opt:"-p"`
Attachments bool `opt:"-a"`
AllAttachments bool `opt:"-A"`
- Path string `opt:"..." required:"false" metavar:"<path>"`
+ Path string `opt:"path" required:"false" complete:"CompletePath"`
}
func init() {
@@ -37,14 +37,12 @@ func (Save) Aliases() []string {
return []string{"save"}
}
-func (s Save) Complete(args []string) []string {
- trimmed := commands.Operands(args, s.Options())
- path := strings.Join(trimmed, " ")
+func (*Save) CompletePath(arg string) []string {
defaultPath := config.General.DefaultSavePath
- if defaultPath != "" && !isAbsPath(path) {
- path = filepath.Join(defaultPath, path)
+ if defaultPath != "" && !isAbsPath(arg) {
+ arg = filepath.Join(defaultPath, arg)
}
- return commands.CompletePath(xdg.ExpandHome(path))
+ return commands.CompletePath(xdg.ExpandHome(arg))
}
func (s Save) Execute(args []string) error {
diff --git a/commands/msgview/toggle-headers.go b/commands/msgview/toggle-headers.go
index c27307d3..37f5b22f 100644
--- a/commands/msgview/toggle-headers.go
+++ b/commands/msgview/toggle-headers.go
@@ -14,10 +14,6 @@ func (ToggleHeaders) Aliases() []string {
return []string{"toggle-headers"}
}
-func (ToggleHeaders) Complete(args []string) []string {
- return nil
-}
-
func (ToggleHeaders) Execute(args []string) error {
mv, _ := app.SelectedTabContent().(*app.MessageViewer)
mv.ToggleHeaders()
diff --git a/commands/msgview/toggle-key-passthrough.go b/commands/msgview/toggle-key-passthrough.go
index 32735870..b4c5f2c7 100644
--- a/commands/msgview/toggle-key-passthrough.go
+++ b/commands/msgview/toggle-key-passthrough.go
@@ -15,10 +15,6 @@ func (ToggleKeyPassthrough) Aliases() []string {
return []string{"toggle-key-passthrough"}
}
-func (ToggleKeyPassthrough) Complete(args []string) []string {
- return nil
-}
-
func (ToggleKeyPassthrough) Execute(args []string) error {
mv, _ := app.SelectedTabContent().(*app.MessageViewer)
keyPassthroughEnabled := mv.ToggleKeyPassthrough()
diff --git a/commands/new-account.go b/commands/new-account.go
index b30e0e34..1a14a821 100644
--- a/commands/new-account.go
+++ b/commands/new-account.go
@@ -16,10 +16,6 @@ func (NewAccount) Aliases() []string {
return []string{"new-account"}
}
-func (NewAccount) Complete(args []string) []string {
- return nil
-}
-
func (n NewAccount) Execute(args []string) error {
wizard := app.NewAccountWizard()
wizard.ConfigureTemporaryAccount(n.Temp)
diff --git a/commands/next-tab.go b/commands/next-tab.go
index d8374191..f1f34d22 100644
--- a/commands/next-tab.go
+++ b/commands/next-tab.go
@@ -16,10 +16,6 @@ func (NextPrevTab) Aliases() []string {
return []string{"next-tab", "prev-tab"}
}
-func (NextPrevTab) Complete(args []string) []string {
- return nil
-}
-
func (np NextPrevTab) Execute(args []string) error {
for n := 0; n < np.Offset; n++ {
if args[0] == "prev-tab" {
diff --git a/commands/parser.go b/commands/parser.go
deleted file mode 100644
index e8146506..00000000
--- a/commands/parser.go
+++ /dev/null
@@ -1,134 +0,0 @@
-package commands
-
-import (
- "strings"
-)
-
-type completionType int
-
-const (
- NONE completionType = iota
- COMMAND
- OPERAND
- SHORT_OPTION
- OPTION_ARGUMENT
-)
-
-type parser struct {
- tokens []string
- optind int
- spec string
- space bool
- kind completionType
- flag string
- arg string
- err error
-}
-
-func newParser(cmd, spec string, spaceTerminated bool) (*parser, error) {
- args, err := splitCmd(cmd)
- if err != nil {
- return nil, err
- }
-
- p := &parser{
- tokens: args,
- optind: 0,
- spec: spec,
- space: spaceTerminated,
- kind: NONE,
- flag: "",
- arg: "",
- err: nil,
- }
-
- state := command
- for state != nil {
- state = state(p)
- }
-
- return p, p.err
-}
-
-func (p *parser) empty() bool {
- return len(p.tokens) == 0
-}
-
-func (p *parser) peek() string {
- return p.tokens[0]
-}
-
-func (p *parser) advance() string {
- if p.empty() {
- return ""
- }
- tok := p.tokens[0]
- p.tokens = p.tokens[1:]
- p.optind++
- return tok
-}
-
-func (p *parser) set(t completionType) {
- p.kind = t
-}
-
-func (p *parser) hasArgument() bool {
- n := len(p.flag)
- if n > 0 {
- s := string(p.flag[n-1]) + ":"
- return strings.Contains(p.spec, s)
- }
- return false
-}
-
-type stateFn func(*parser) stateFn
-
-func command(p *parser) stateFn {
- p.set(COMMAND)
- p.advance()
- return peek(p)
-}
-
-func peek(p *parser) stateFn {
- if p.empty() {
- if p.space {
- return operand
- }
- return nil
- }
- if p.spec == "" {
- return operand
- }
- s := p.peek()
- switch {
- case s == "--":
- p.advance()
- case strings.HasPrefix(s, "-"):
- return short_option
- }
- return operand
-}
-
-func short_option(p *parser) stateFn {
- p.set(SHORT_OPTION)
- tok := p.advance()
- p.flag = tok[1:]
- if p.hasArgument() {
- return option_argument
- }
- return peek(p)
-}
-
-func option_argument(p *parser) stateFn {
- p.set(OPTION_ARGUMENT)
- p.arg = p.advance()
- if p.empty() && len(p.arg) == 0 {
- return nil
- }
- return peek(p)
-}
-
-func operand(p *parser) stateFn {
- p.set(OPERAND)
- return nil
-}
diff --git a/commands/parser_test.go b/commands/parser_test.go
deleted file mode 100644
index d6ccd385..00000000
--- a/commands/parser_test.go
+++ /dev/null
@@ -1,258 +0,0 @@
-package commands
-
-import (
- "testing"
-)
-
-var parserTests = []struct {
- name string
- cmd string
- wantType completionType
- wantFlag string
- wantArg string
- wantOptind int
-}{
- {
- name: "empty command",
- cmd: "",
- wantType: COMMAND,
- wantFlag: "",
- wantArg: "",
- wantOptind: 0,
- },
- {
- name: "command only",
- cmd: "cmd",
- wantType: COMMAND,
- wantFlag: "",
- wantArg: "",
- wantOptind: 1,
- },
- {
- name: "with space",
- cmd: "cmd ",
- wantType: OPERAND,
- wantFlag: "",
- wantArg: "",
- wantOptind: 1,
- },
- {
- name: "with two spaces",
- cmd: "cmd ",
- wantType: OPERAND,
- wantFlag: "",
- wantArg: "",
- wantOptind: 1,
- },
- {
- name: "with single option flag",
- cmd: "cmd -",
- wantType: SHORT_OPTION,
- wantFlag: "",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with single option flag two spaces",
- cmd: "cmd -",
- wantType: SHORT_OPTION,
- wantFlag: "",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with single option flag completed",
- cmd: "cmd -a",
- wantType: SHORT_OPTION,
- wantFlag: "a",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with single option flag completed and space",
- cmd: "cmd -a ",
- wantType: OPERAND,
- wantFlag: "a",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with single option flag completed and two spaces",
- cmd: "cmd -a ",
- wantType: OPERAND,
- wantFlag: "a",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with two single option flag completed",
- cmd: "cmd -b -a",
- wantType: SHORT_OPTION,
- wantFlag: "a",
- wantArg: "",
- wantOptind: 3,
- },
- {
- name: "with two single option flag combined",
- cmd: "cmd -ab",
- wantType: SHORT_OPTION,
- wantFlag: "ab",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with two single option flag and space",
- cmd: "cmd -ab ",
- wantType: OPERAND,
- wantFlag: "ab",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with mandatory option flag",
- cmd: "cmd -f",
- wantType: OPTION_ARGUMENT,
- wantFlag: "f",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with mandatory option flag and space",
- cmd: "cmd -f ",
- wantType: OPTION_ARGUMENT,
- wantFlag: "f",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with mandatory option flag and two spaces",
- cmd: "cmd -f ",
- wantType: OPTION_ARGUMENT,
- wantFlag: "f",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with mandatory option flag and completed",
- cmd: "cmd -f a",
- wantType: OPTION_ARGUMENT,
- wantFlag: "f",
- wantArg: "a",
- wantOptind: 3,
- },
- {
- name: "with mandatory option flag and completed quote",
- cmd: "cmd -f 'a b'",
- wantType: OPTION_ARGUMENT,
- wantFlag: "f",
- wantArg: "a b",
- wantOptind: 3,
- },
- {
- name: "with mandatory option flag and operand",
- cmd: "cmd -f 'a b' hello",
- wantType: OPERAND,
- wantFlag: "f",
- wantArg: "a b",
- wantOptind: 3,
- },
- {
- name: "with mandatory option flag and two spaces between",
- cmd: "cmd -f a",
- wantType: OPTION_ARGUMENT,
- wantFlag: "f",
- wantArg: "a",
- wantOptind: 3,
- },
- {
- name: "with mandatory option flag and more spaces",
- cmd: "cmd -f a ",
- wantType: OPERAND,
- wantFlag: "f",
- wantArg: "a",
- wantOptind: 3,
- },
- {
- name: "with template data",
- cmd: "cmd -a {{if .Size}} hello {{else}} {{end}}",
- wantType: OPERAND,
- wantFlag: "a",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with operand",
- cmd: "cmd -ab /tmp/aerc-",
- wantType: OPERAND,
- wantFlag: "ab",
- wantArg: "",
- wantOptind: 2,
- },
- {
- name: "with operand indicator",
- cmd: "cmd -ab -- /tmp/aerc-",
- wantType: OPERAND,
- wantFlag: "ab",
- wantArg: "",
- wantOptind: 3,
- },
- {
- name: "hyphen connected command",
- cmd: "cmd-dmc",
- wantType: COMMAND,
- wantFlag: "",
- wantArg: "",
- wantOptind: 1,
- },
- {
- name: "incomplete hyphen connected command",
- cmd: "cmd-",
- wantType: COMMAND,
- wantFlag: "",
- wantArg: "",
- wantOptind: 1,
- },
- {
- name: "hyphen connected command with option",
- cmd: "cmd-dmc -a",
- wantType: SHORT_OPTION,
- wantFlag: "a",
- wantArg: "",
- wantOptind: 2,
- },
-}
-
-func TestCommands_Parser(t *testing.T) {
- for i, test := range parserTests {
- n := len(test.cmd)
- spaceTerminated := n > 0 && test.cmd[n-1] == ' '
- parser, err := newParser(test.cmd, "abf:", spaceTerminated)
- if err != nil {
- t.Errorf("parser error: %v", err)
- }
-
- if test.wantType != parser.kind {
- t.Errorf("test %d '%s': completion type does not match: "+
- "want %d, but got %d", i, test.cmd, test.wantType,
- parser.kind)
- }
-
- if test.wantFlag != parser.flag {
- t.Errorf("test %d '%s': flag does not match: "+
- "want %s, but got %s", i, test.cmd, test.wantFlag,
- parser.flag)
- }
-
- if test.wantArg != parser.arg {
- t.Errorf("test %d '%s': arg does not match: "+
- "want %s, but got %s", i, test.cmd, test.wantArg,
- parser.arg)
- }
-
- if test.wantOptind != parser.optind {
- t.Errorf("test %d '%s': optind does not match: "+
- "want %d, but got %d", i, test.cmd, test.wantOptind,
- parser.optind)
- }
- }
-}
diff --git a/commands/pin-tab.go b/commands/pin-tab.go
index 276442ce..d5013bf6 100644
--- a/commands/pin-tab.go
+++ b/commands/pin-tab.go
@@ -14,10 +14,6 @@ func (PinTab) Aliases() []string {
return []string{"pin-tab", "unpin-tab"}
}
-func (PinTab) Complete(args []string) []string {
- return nil
-}
-
func (PinTab) Execute(args []string) error {
switch args[0] {
case "pin-tab":
diff --git a/commands/prompt.go b/commands/prompt.go
index dd259c30..d791f7a9 100644
--- a/commands/prompt.go
+++ b/commands/prompt.go
@@ -1,8 +1,6 @@
package commands
import (
- "strings"
-
"git.sr.ht/~rjarry/go-opt"
"git.sr.ht/~rjarry/aerc/app"
@@ -10,7 +8,7 @@ import (
type Prompt struct {
Text string `opt:"text"`
- Cmd []string `opt:"..."`
+ Cmd []string `opt:"..." complete:"CompleteCommand"`
}
func init() {
@@ -21,56 +19,8 @@ func (Prompt) Aliases() []string {
return []string{"prompt"}
}
-func (Prompt) Complete(args []string) []string {
- argc := len(args)
- if argc == 0 {
- return nil
- }
- hascommand := argc > 2
- if argc == 1 {
- args = append(args, "")
- }
-
- cmd := GlobalCommands.ByName(args[1])
- var cs []string
- if cmd != nil {
- cs = cmd.Complete(args[2:])
- hascommand = true
- } else {
- if hascommand {
- return nil
- }
- cs = GlobalCommands.Names()
- }
- if cs == nil {
- return nil
- }
-
- var b strings.Builder
- // it seems '' quoting is enough
- // to keep quoted arguments in one piece
- b.WriteRune('\'')
- b.WriteString(args[0])
- b.WriteRune('\'')
- b.WriteRune(' ')
- if hascommand {
- b.WriteString(args[1])
- b.WriteRune(' ')
- }
-
- src := b.String()
- b.Reset()
-
- rs := make([]string, 0, len(cs))
- for _, c := range cs {
- b.WriteString(src)
- b.WriteString(c)
-
- rs = append(rs, b.String())
- b.Reset()
- }
-
- return rs
+func (*Prompt) CompleteCommand(arg string) []string {
+ return CompletionFromList(GlobalCommands.Names(), arg)
}
func (p Prompt) Execute(args []string) error {
diff --git a/commands/pwd.go b/commands/pwd.go
index 426be78b..2b1961a1 100644
--- a/commands/pwd.go
+++ b/commands/pwd.go
@@ -17,10 +17,6 @@ func (PrintWorkDir) Aliases() []string {
return []string{"pwd"}
}
-func (PrintWorkDir) Complete(args []string) []string {
- return nil
-}
-
func (PrintWorkDir) Execute(args []string) error {
pwd, err := os.Getwd()
if err != nil {
diff --git a/commands/quit.go b/commands/quit.go
index 3e0f2968..e96c9253 100644
--- a/commands/quit.go
+++ b/commands/quit.go
@@ -18,10 +18,6 @@ func (Quit) Aliases() []string {
return []string{"quit", "exit"}
}
-func (Quit) Complete(args []string) []string {
- return nil
-}
-
type ErrorExit int
func (err ErrorExit) Error() string {
diff --git a/commands/send-keys.go b/commands/send-keys.go
index 9541b88d..bb872a33 100644
--- a/commands/send-keys.go
+++ b/commands/send-keys.go
@@ -19,10 +19,6 @@ func (SendKeys) Aliases() []string {
return []string{"send-keys"}
}
-func (SendKeys) Complete(args []string) []string {
- return nil
-}
-
func (s SendKeys) Execute(args []string) error {
tab, ok := app.SelectedTabContent().(app.HasTerminal)
if !ok {
diff --git a/commands/suspend.go b/commands/suspend.go
index 80d59028..a8996441 100644
--- a/commands/suspend.go
+++ b/commands/suspend.go
@@ -12,10 +12,6 @@ func (Suspend) Aliases() []string {
return []string{"suspend"}
}
-func (Suspend) Complete(args []string) []string {
- return nil
-}
-
func (Suspend) Execute(args []string) error {
ui.QueueSuspend()
return nil
diff --git a/commands/term.go b/commands/term.go
index 225fee57..3f8f9875 100644
--- a/commands/term.go
+++ b/commands/term.go
@@ -21,10 +21,6 @@ func (Term) Aliases() []string {
return []string{"terminal", "term"}
}
-func (Term) Complete(args []string) []string {
- return nil
-}
-
func (t Term) Execute(args []string) error {
if len(t.Cmd) == 0 {
shell, err := loginshell.Shell()
diff --git a/commands/terminal/close.go b/commands/terminal/close.go
index 913a3387..610a8463 100644
--- a/commands/terminal/close.go
+++ b/commands/terminal/close.go
@@ -14,10 +14,6 @@ func (Close) Aliases() []string {
return []string{"close"}
}
-func (Close) Complete(args []string) []string {
- return nil
-}
-
func (Close) Execute(args []string) error {
term, _ := app.SelectedTabContent().(*app.Terminal)
term.Close()
diff --git a/commands/util.go b/commands/util.go
index e9a377a6..a9aa81b3 100644
--- a/commands/util.go
+++ b/commands/util.go
@@ -18,6 +18,7 @@ import (
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
+ "git.sr.ht/~rjarry/go-opt"
"github.com/gdamore/tcell/v2"
)
@@ -93,7 +94,7 @@ func CompletePath(path string) []string {
}
if !strings.HasPrefix(path, ".") && !strings.Contains(path, "/.") {
- log.Debugf("removing hidden files from glob results")
+ log.Tracef("removing hidden files from glob results")
for i := len(matches) - 1; i >= 0; i-- {
if strings.HasPrefix(filepath.Base(matches[i]), ".") {
if i == len(matches)-1 {
@@ -107,11 +108,13 @@ func CompletePath(path string) []string {
for i, m := range matches {
if isDir(m) {
- matches[i] = m + "/"
+ m += "/"
}
+ matches[i] = opt.QuoteArg((xdg.TildeHome(m)))
}
sort.Strings(matches)
+
return matches
}
@@ -122,11 +125,11 @@ func CompletePath(path string) []string {
if isDir(f) {
f += "/"
}
-
- files[i] = f
+ files[i] = opt.QuoteArg((xdg.TildeHome(f)))
}
sort.Strings(files)
+
return files
}
@@ -227,16 +230,16 @@ func MsgInfoFromUids(store *lib.MessageStore, uids []uint32, statusInfo func(str
// FilterList takes a list of valid completions and filters it, either
// by case smart prefix, or by fuzzy matching, prepending "prefix" to each completion
-func FilterList(valid []string, search, prefix string, isFuzzy bool) []string {
- out := make([]string, 0)
+func FilterList(valid []string, search, prefix, suffix string, isFuzzy bool) []string {
+ out := make([]string, 0, len(valid))
if isFuzzy {
for _, v := range fuzzy.RankFindFold(search, valid) {
- out = append(out, prefix+v.Target)
+ out = append(out, opt.QuoteArg(prefix+v.Target+suffix))
}
} else {
for _, v := range valid {
if hasCaseSmartPrefix(v, search) {
- out = append(out, prefix+v)
+ out = append(out, opt.QuoteArg(prefix+v+suffix))
}
}
}
diff --git a/commands/z.go b/commands/z.go
index 5aee2a2c..2ea65e0c 100644
--- a/commands/z.go
+++ b/commands/z.go
@@ -8,7 +8,8 @@ import (
)
type Zoxide struct {
- Target string `opt:"..." default:"~" metavar:"<folder> | <query>..."`
+ Target string `opt:"folder" default:"~" complete:"CompleteFolder"`
+ Args []string `opt:"..." required:"false" metavar:"<query>..."`
}
func ZoxideAdd(arg string) error {
@@ -36,8 +37,8 @@ func (Zoxide) Aliases() []string {
return []string{"z"}
}
-func (Zoxide) Complete(args []string) []string {
- return ChangeDirectory{}.Complete(args)
+func (*Zoxide) CompleteFolder(arg string) []string {
+ return GetFolders(arg)
}
// Execute calls zoxide add and query and delegates actually changing the
diff --git a/main.go b/main.go
index dc7d0267..75069e13 100644
--- a/main.go
+++ b/main.go
@@ -143,7 +143,7 @@ func getCompletions(cmdline string) ([]string, string) {
for _, set := range cmds {
for _, n := range set.Names() {
if strings.HasPrefix(n, cmdline) {
- completions = append(completions, n)
+ completions = append(completions, n+" ")
}
}
}