aboutsummaryrefslogtreecommitdiffstats
path: root/commands/msg
diff options
context:
space:
mode:
Diffstat (limited to 'commands/msg')
-rw-r--r--commands/msg/archive.go24
-rw-r--r--commands/msg/copy.go28
-rw-r--r--commands/msg/delete.go5
-rw-r--r--commands/msg/envelope.go29
-rw-r--r--commands/msg/fold.go4
-rw-r--r--commands/msg/forward.go54
-rw-r--r--commands/msg/invite.go25
-rw-r--r--commands/msg/mark.go68
-rw-r--r--commands/msg/modify-labels.go14
-rw-r--r--commands/msg/move.go29
-rw-r--r--commands/msg/pipe.go80
-rw-r--r--commands/msg/read.go132
-rw-r--r--commands/msg/recall.go34
-rw-r--r--commands/msg/reply.go65
-rw-r--r--commands/msg/toggle-thread-context.go5
-rw-r--r--commands/msg/toggle-threads.go5
-rw-r--r--commands/msg/unsubscribe.go26
17 files changed, 202 insertions, 425 deletions
diff --git a/commands/msg/archive.go b/commands/msg/archive.go
index f326d0c6..f4d6e3be 100644
--- a/commands/msg/archive.go
+++ b/commands/msg/archive.go
@@ -1,7 +1,6 @@
package msg
import (
- "errors"
"fmt"
"strings"
"sync"
@@ -19,7 +18,21 @@ const (
ARCHIVE_MONTH = "month"
)
-type Archive struct{}
+var ARCHIVE_TYPES = []string{ARCHIVE_FLAT, ARCHIVE_YEAR, ARCHIVE_MONTH}
+
+type Archive struct {
+ Type string `opt:"type" action:"ParseArchiveType" metavar:"flat|year|month"`
+}
+
+func (a *Archive) ParseArchiveType(arg string) error {
+ for _, t := range ARCHIVE_TYPES {
+ if t == arg {
+ a.Type = arg
+ return nil
+ }
+ }
+ return fmt.Errorf("invalid archive type")
+}
func init() {
register(Archive{})
@@ -34,16 +47,13 @@ func (Archive) Complete(args []string) []string {
return commands.CompletionFromList(valid, args)
}
-func (Archive) Execute(args []string) error {
- if len(args) != 2 {
- return errors.New("Usage: archive <flat|year|month>")
- }
+func (a Archive) Execute(args []string) error {
h := newHelper()
msgs, err := h.messages()
if err != nil {
return err
}
- err = archive(msgs, args[1])
+ err = archive(msgs, a.Type)
return err
}
diff --git a/commands/msg/copy.go b/commands/msg/copy.go
index 1a902772..4109ef99 100644
--- a/commands/msg/copy.go
+++ b/commands/msg/copy.go
@@ -1,18 +1,17 @@
package msg
import (
- "errors"
- "strings"
"time"
- "git.sr.ht/~sircmpwn/getopt"
-
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/commands"
"git.sr.ht/~rjarry/aerc/worker/types"
)
-type Copy struct{}
+type Copy struct {
+ CreateFolders bool `opt:"-p"`
+ Folder string `opt:"..." metavar:"<folder>"`
+}
func init() {
register(Copy{})
@@ -26,20 +25,7 @@ func (Copy) Complete(args []string) []string {
return commands.GetFolders(args)
}
-func (Copy) Execute(args []string) error {
- if len(args) == 1 {
- return errors.New("Usage: cp [-p] <folder>")
- }
- opts, optind, err := getopt.Getopts(args, "p")
- if err != nil {
- return err
- }
- var createParents bool
- for _, opt := range opts {
- if opt.Option == 'p' {
- createParents = true
- }
- }
+func (c Copy) Execute(args []string) error {
h := newHelper()
uids, err := h.markedOrSelectedUids()
if err != nil {
@@ -49,8 +35,8 @@ func (Copy) Execute(args []string) error {
if err != nil {
return err
}
- store.Copy(uids, strings.Join(args[optind:], " "),
- createParents, func(
+ store.Copy(uids, c.Folder,
+ c.CreateFolders, func(
msg types.WorkerMessage,
) {
switch msg := msg.(type) {
diff --git a/commands/msg/delete.go b/commands/msg/delete.go
index 107c8a3f..49463abc 100644
--- a/commands/msg/delete.go
+++ b/commands/msg/delete.go
@@ -1,7 +1,6 @@
package msg
import (
- "errors"
"time"
"git.sr.ht/~rjarry/aerc/app"
@@ -27,10 +26,6 @@ func (Delete) Complete(args []string) []string {
}
func (Delete) Execute(args []string) error {
- if len(args) != 1 {
- return errors.New("Usage: :delete")
- }
-
h := newHelper()
store, err := h.store()
if err != nil {
diff --git a/commands/msg/envelope.go b/commands/msg/envelope.go
index 3a388c12..6da82a1e 100644
--- a/commands/msg/envelope.go
+++ b/commands/msg/envelope.go
@@ -9,11 +9,13 @@ import (
"git.sr.ht/~rjarry/aerc/lib/format"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
- "git.sr.ht/~sircmpwn/getopt"
"github.com/emersion/go-message/mail"
)
-type Envelope struct{}
+type Envelope struct {
+ Header bool `opt:"-h"`
+ Format string `opt:"-s" default:"%-20.20s: %s"`
+}
func init() {
register(Envelope{})
@@ -27,22 +29,7 @@ func (Envelope) Complete(args []string) []string {
return nil
}
-func (Envelope) Execute(args []string) error {
- header := false
- fmtStr := "%-20.20s: %s"
- opts, _, err := getopt.Getopts(args, "hs:")
- if err != nil {
- return err
- }
- for _, opt := range opts {
- switch opt.Option {
- case 's':
- fmtStr = opt.Value
- case 'h':
- header = true
- }
- }
-
+func (e Envelope) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
@@ -53,10 +40,10 @@ func (Envelope) Execute(args []string) error {
return err
} else {
if msg != nil {
- if header {
- list = parseHeader(msg, fmtStr)
+ if e.Header {
+ list = parseHeader(msg, e.Format)
} else {
- list = parseEnvelope(msg, fmtStr,
+ list = parseEnvelope(msg, e.Format,
acct.UiConfig().TimestampFormat)
}
} else {
diff --git a/commands/msg/fold.go b/commands/msg/fold.go
index 1d40b90a..0621c8c3 100644
--- a/commands/msg/fold.go
+++ b/commands/msg/fold.go
@@ -2,7 +2,6 @@ package msg
import (
"errors"
- "fmt"
"strings"
"git.sr.ht/~rjarry/aerc/lib/ui"
@@ -23,9 +22,6 @@ func (Fold) Complete(args []string) []string {
}
func (Fold) Execute(args []string) error {
- if len(args) != 1 {
- return fmt.Errorf("Usage: %s", args[0])
- }
h := newHelper()
store, err := h.store()
if err != nil {
diff --git a/commands/msg/forward.go b/commands/msg/forward.go
index 68f162cc..e6e386a9 100644
--- a/commands/msg/forward.go
+++ b/commands/msg/forward.go
@@ -20,11 +20,16 @@ import (
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
"github.com/emersion/go-message/mail"
-
- "git.sr.ht/~sircmpwn/getopt"
)
-type forward struct{}
+type forward struct {
+ AttachAll bool `opt:"-A"`
+ AttachFull bool `opt:"-F"`
+ Edit bool `opt:"-e"`
+ NoEdit bool `opt:"-E"`
+ Template string `opt:"-T"`
+ To []string `opt:"..." required:"false"`
+}
func init() {
register(forward{})
@@ -38,36 +43,11 @@ func (forward) Complete(args []string) []string {
return nil
}
-func (forward) Execute(args []string) error {
- opts, optind, err := getopt.Getopts(args, "AFT:eE")
- if err != nil {
- return err
- }
- if len(args) != optind {
- return errors.New("Usage: forward [-A|-F] [-T <template>] [-e|-E]")
- }
- attachAll := false
- attachFull := false
- template := ""
- editHeaders := config.Compose.EditHeaders
- for _, opt := range opts {
- switch opt.Option {
- case 'A':
- attachAll = true
- case 'F':
- attachFull = true
- case 'T':
- template = opt.Value
- case 'e':
- editHeaders = true
- case 'E':
- editHeaders = false
- }
- }
-
- if attachAll && attachFull {
+func (f forward) Execute(args []string) error {
+ if f.AttachAll && f.AttachFull {
return errors.New("Options -A and -F are mutually exclusive")
}
+ editHeaders := (config.Compose.EditHeaders || f.Edit) && !f.NoEdit
widget := app.SelectedTabContent().(app.ProvidesMessage)
acct := widget.SelectedAccount()
@@ -89,7 +69,7 @@ func (forward) Execute(args []string) error {
h.SetSubject(subject)
var tolist []*mail.Address
- to := strings.Join(args[optind:], ", ")
+ to := strings.Join(f.To, ", ")
if strings.Contains(to, "@") {
tolist, err = mail.ParseAddressList(to)
if err != nil {
@@ -109,7 +89,7 @@ func (forward) Execute(args []string) error {
addTab := func() (*app.Composer, error) {
composer, err := app.NewComposer(acct,
acct.AccountConfig(), acct.Worker(), editHeaders,
- template, h, &original, nil)
+ f.Template, h, &original, nil)
if err != nil {
app.PushError("Error: " + err.Error())
return nil, err
@@ -124,7 +104,7 @@ func (forward) Execute(args []string) error {
return composer, nil
}
- if attachFull {
+ if f.AttachFull {
tmpDir, err := os.MkdirTemp("", "aerc-tmp-attachment")
if err != nil {
return err
@@ -158,8 +138,8 @@ func (forward) Execute(args []string) error {
})
})
} else {
- if template == "" {
- template = config.Templates.Forwards
+ if f.Template == "" {
+ f.Template = config.Templates.Forwards
}
part := lib.FindPlaintext(msg.BodyStructure, nil)
@@ -186,7 +166,7 @@ func (forward) Execute(args []string) error {
}
// add attachments
- if attachAll {
+ if f.AttachAll {
var mu sync.Mutex
parts := lib.FindAllNonMultipart(msg.BodyStructure, nil, nil)
for _, p := range parts {
diff --git a/commands/msg/invite.go b/commands/msg/invite.go
index 60107480..5b8558b0 100644
--- a/commands/msg/invite.go
+++ b/commands/msg/invite.go
@@ -12,11 +12,13 @@ import (
"git.sr.ht/~rjarry/aerc/lib/format"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/models"
- "git.sr.ht/~sircmpwn/getopt"
"github.com/emersion/go-message/mail"
)
-type invite struct{}
+type invite struct {
+ Edit bool `opt:"-e"`
+ NoEdit bool `opt:"-E"`
+}
func init() {
register(invite{})
@@ -30,7 +32,7 @@ func (invite) Complete(args []string) []string {
return nil
}
-func (invite) Execute(args []string) error {
+func (i invite) Execute(args []string) error {
acct := app.SelectedAccount()
if acct == nil {
return errors.New("no account selected")
@@ -49,22 +51,7 @@ func (invite) Execute(args []string) error {
return fmt.Errorf("no invitation found (missing text/calendar)")
}
- editHeaders := config.Compose.EditHeaders
- opts, optind, err := getopt.Getopts(args, "eE")
- if err != nil {
- return err
- }
- if len(args) != optind {
- return errors.New("Usage: accept|accept-tentative|decline [-e|-E]")
- }
- for _, opt := range opts {
- switch opt.Option {
- case 'e':
- editHeaders = true
- case 'E':
- editHeaders = false
- }
- }
+ editHeaders := (config.Compose.EditHeaders || i.Edit) && !i.NoEdit
subject := trimLocalizedRe(msg.Envelope.Subject, acct.AccountConfig().LocalizedRe)
switch args[0] {
diff --git a/commands/msg/mark.go b/commands/msg/mark.go
index eeaa5485..c2c21cf2 100644
--- a/commands/msg/mark.go
+++ b/commands/msg/mark.go
@@ -2,11 +2,15 @@ package msg
import (
"fmt"
-
- "git.sr.ht/~sircmpwn/getopt"
)
-type Mark struct{}
+type Mark struct {
+ All bool `opt:"-a" aliases:"mark,unmark"`
+ Toggle bool `opt:"-t" aliases:"mark,unmark"`
+ Visual bool `opt:"-v" aliases:"mark,unmark"`
+ VisualClear bool `opt:"-V" aliases:"mark,unmark"`
+ Thread bool `opt:"-T" aliases:"mark,unmark"`
+}
func init() {
register(Mark{})
@@ -20,7 +24,7 @@ func (Mark) Complete(args []string) []string {
return nil
}
-func (Mark) Execute(args []string) error {
+func (m Mark) Execute(args []string) error {
h := newHelper()
OnSelectedMessage := func(fn func(uint32)) error {
if fn == nil {
@@ -38,63 +42,38 @@ func (Mark) Execute(args []string) error {
return err
}
marker := store.Marker()
- opts, _, err := getopt.Getopts(args, "atvVT")
- if err != nil {
- return err
- }
- var all bool
- var toggle bool
- var visual bool
- var clearVisual bool
- var thread bool
- for _, opt := range opts {
- switch opt.Option {
- case 'a':
- all = true
- case 'v':
- visual = true
- clearVisual = true
- case 'V':
- visual = true
- case 't':
- toggle = true
- case 'T':
- thread = true
- }
- }
- if thread && all {
+ if m.Thread && m.All {
return fmt.Errorf("-a and -T are mutually exclusive")
}
- if thread && visual {
+ if m.Thread && (m.Visual || m.VisualClear) {
return fmt.Errorf("-v and -T are mutually exclusive")
}
+ if m.Visual && m.All {
+ return fmt.Errorf("-a and -v are mutually exclusive")
+ }
switch args[0] {
case "mark":
- if all && visual {
- return fmt.Errorf("-a and -v are mutually exclusive")
- }
-
var modFunc func(uint32)
- if toggle {
+ if m.Toggle {
modFunc = marker.ToggleMark
} else {
modFunc = marker.Mark
}
switch {
- case all:
+ case m.All:
uids := store.Uids()
for _, uid := range uids {
modFunc(uid)
}
return nil
- case visual:
- marker.ToggleVisualMark(clearVisual)
+ case m.Visual || m.VisualClear:
+ marker.ToggleVisualMark(m.VisualClear)
return nil
default:
- if thread {
+ if m.Thread {
threadPtr, err := store.SelectedThread()
if err != nil {
return err
@@ -109,22 +88,22 @@ func (Mark) Execute(args []string) error {
}
case "unmark":
- if visual {
+ if m.Visual || m.VisualClear {
return fmt.Errorf("visual mode not supported for this command")
}
switch {
- case all && toggle:
+ case m.All && m.Toggle:
uids := store.Uids()
for _, uid := range uids {
marker.ToggleMark(uid)
}
return nil
- case all && !toggle:
+ case m.All && !m.Toggle:
marker.ClearVisualMark()
return nil
default:
- if thread {
+ if m.Thread {
threadPtr, err := store.SelectedThread()
if err != nil {
return err
@@ -138,9 +117,6 @@ func (Mark) Execute(args []string) error {
return nil
}
case "remark":
- if all || visual || toggle || thread {
- return fmt.Errorf("Usage: :remark")
- }
marker.Remark()
return nil
}
diff --git a/commands/msg/modify-labels.go b/commands/msg/modify-labels.go
index d219a57e..6fdbeac4 100644
--- a/commands/msg/modify-labels.go
+++ b/commands/msg/modify-labels.go
@@ -1,7 +1,6 @@
package msg
import (
- "errors"
"time"
"git.sr.ht/~rjarry/aerc/app"
@@ -9,7 +8,9 @@ import (
"git.sr.ht/~rjarry/aerc/worker/types"
)
-type ModifyLabels struct{}
+type ModifyLabels struct {
+ Labels []string `opt:"..." metavar:"[+-]<label>"`
+}
func init() {
register(ModifyLabels{})
@@ -23,12 +24,7 @@ func (ModifyLabels) Complete(args []string) []string {
return commands.GetLabels(args)
}
-func (ModifyLabels) Execute(args []string) error {
- changes := args[1:]
- if len(changes) == 0 {
- return errors.New("Usage: modify-labels <[+-]label> ...")
- }
-
+func (m ModifyLabels) Execute(args []string) error {
h := newHelper()
store, err := h.store()
if err != nil {
@@ -40,7 +36,7 @@ func (ModifyLabels) Execute(args []string) error {
}
var add, remove []string
- for _, l := range changes {
+ for _, l := range m.Labels {
switch l[0] {
case '+':
add = append(add, l[1:])
diff --git a/commands/msg/move.go b/commands/msg/move.go
index 5ef9390a..1dd68d35 100644
--- a/commands/msg/move.go
+++ b/commands/msg/move.go
@@ -1,8 +1,6 @@
package msg
import (
- "errors"
- "strings"
"time"
"git.sr.ht/~rjarry/aerc/app"
@@ -12,10 +10,12 @@ import (
"git.sr.ht/~rjarry/aerc/lib/ui"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
- "git.sr.ht/~sircmpwn/getopt"
)
-type Move struct{}
+type Move struct {
+ CreateFolders bool `opt:"-p"`
+ Folder string `opt:"..." metavar:"<folder>"`
+}
func init() {
register(Move{})
@@ -29,21 +29,7 @@ func (Move) Complete(args []string) []string {
return commands.GetFolders(args)
}
-func (Move) Execute(args []string) error {
- if len(args) == 1 {
- return errors.New("Usage: mv [-p] <folder>")
- }
- opts, optind, err := getopt.Getopts(args, "p")
- if err != nil {
- return err
- }
- var createParents bool
- for _, opt := range opts {
- if opt.Option == 'p' {
- createParents = true
- }
- }
-
+func (m Move) Execute(args []string) error {
h := newHelper()
acct, err := h.account()
if err != nil {
@@ -64,14 +50,13 @@ func (Move) Execute(args []string) error {
marker := store.Marker()
marker.ClearVisualMark()
next := findNextNonDeleted(uids, store)
- joinedArgs := strings.Join(args[optind:], " ")
- store.Move(uids, joinedArgs, createParents, func(
+ store.Move(uids, m.Folder, m.CreateFolders, func(
msg types.WorkerMessage,
) {
switch msg := msg.(type) {
case *types.Done:
- handleDone(acct, next, "Messages moved to "+joinedArgs, store)
+ handleDone(acct, next, "Messages moved to "+m.Folder, store)
case *types.Error:
app.PushError(msg.Error.Error())
marker.Remark()
diff --git a/commands/msg/pipe.go b/commands/msg/pipe.go
index c9d88f40..75b63b10 100644
--- a/commands/msg/pipe.go
+++ b/commands/msg/pipe.go
@@ -14,11 +14,14 @@ import (
"git.sr.ht/~rjarry/aerc/log"
mboxer "git.sr.ht/~rjarry/aerc/worker/mbox"
"git.sr.ht/~rjarry/aerc/worker/types"
-
- "git.sr.ht/~sircmpwn/getopt"
)
-type Pipe struct{}
+type Pipe struct {
+ Background bool `opt:"-b"`
+ Full bool `opt:"-m"`
+ Part bool `opt:"-p"`
+ Command []string `opt:"..."`
+}
func init() {
register(Pipe{})
@@ -32,44 +35,17 @@ func (Pipe) Complete(args []string) []string {
return nil
}
-func (Pipe) Execute(args []string) error {
- var (
- background bool
- pipeFull bool
- pipePart bool
- )
- // TODO: let user specify part by index or preferred mimetype
- opts, optind, err := getopt.Getopts(args, "bmp")
- if err != nil {
- return err
- }
- for _, opt := range opts {
- switch opt.Option {
- case 'b':
- background = true
- case 'm':
- if pipePart {
- return errors.New("-m and -p are mutually exclusive")
- }
- pipeFull = true
- case 'p':
- if pipeFull {
- return errors.New("-m and -p are mutually exclusive")
- }
- pipePart = true
- }
- }
- cmd := args[optind:]
- if len(cmd) == 0 {
- return errors.New("Usage: pipe [-mp] <cmd> [args...]")
+func (p Pipe) Execute(args []string) error {
+ if p.Full && p.Part {
+ return errors.New("-m and -p are mutually exclusive")
}
provider := app.SelectedTabContent().(app.ProvidesMessage)
- if !pipeFull && !pipePart {
+ if !p.Full && !p.Part {
if _, ok := provider.(*app.MessageViewer); ok {
- pipePart = true
+ p.Part = true
} else if _, ok := provider.(*app.AccountView); ok {
- pipeFull = true
+ p.Full = true
} else {
return errors.New(
"Neither -m nor -p specified and cannot infer default")
@@ -77,7 +53,7 @@ func (Pipe) Execute(args []string) error {
}
doTerm := func(reader io.Reader, name string) {
- term, err := commands.QuickTerm(cmd, reader)
+ term, err := commands.QuickTerm(p.Command, reader)
if err != nil {
app.PushError(err.Error())
return
@@ -86,7 +62,7 @@ func (Pipe) Execute(args []string) error {
}
doExec := func(reader io.Reader) {
- ecmd := exec.Command(cmd[0], cmd[1:]...)
+ ecmd := exec.Command(p.Command[0], p.Command[1:]...)
pipe, err := ecmd.StdinPipe()
if err != nil {
return
@@ -106,17 +82,19 @@ func (Pipe) Execute(args []string) error {
} else {
if ecmd.ProcessState.ExitCode() != 0 {
app.PushError(fmt.Sprintf(
- "%s: completed with status %d", cmd[0],
+ "%s: completed with status %d", p.Command[0],
ecmd.ProcessState.ExitCode()))
} else {
app.PushStatus(fmt.Sprintf(
- "%s: completed with status %d", cmd[0],
+ "%s: completed with status %d", p.Command[0],
ecmd.ProcessState.ExitCode()), 10*time.Second)
}
}
}
- if pipeFull {
+ app.PushStatus("Fetching messages ...", 10*time.Second)
+
+ if p.Full {
var uids []uint32
var title string
@@ -125,12 +103,12 @@ func (Pipe) Execute(args []string) error {
if err != nil {
if mv, ok := provider.(*app.MessageViewer); ok {
mv.MessageView().FetchFull(func(reader io.Reader) {
- if background {
+ if p.Background {
doExec(reader)
} else {
doTerm(reader,
fmt.Sprintf("%s <%s",
- cmd[0], title))
+ p.Command[0], title))
}
})
return nil
@@ -202,27 +180,27 @@ func (Pipe) Execute(args []string) error {
}
reader := newMessagesReader(messages, len(messages) > 1)
- if background {
+ if p.Background {
doExec(reader)
} else {
- doTerm(reader, fmt.Sprintf("%s <%s", cmd[0], title))
+ doTerm(reader, fmt.Sprintf("%s <%s", p.Command[0], title))
}
}()
- } else if pipePart {
+ } else if p.Part {
mv, ok := provider.(*app.MessageViewer)
if !ok {
return fmt.Errorf("can only pipe message part from a message view")
}
- p := provider.SelectedMessagePart()
- if p == nil {
+ part := provider.SelectedMessagePart()
+ if part == nil {
return fmt.Errorf("could not fetch message part")
}
- mv.MessageView().FetchBodyPart(p.Index, func(reader io.Reader) {
- if background {
+ mv.MessageView().FetchBodyPart(part.Index, func(reader io.Reader) {
+ if p.Background {
doExec(reader)
} else {
name := fmt.Sprintf("%s <%s/[%d]",
- cmd[0], p.Msg.Envelope.Subject, p.Index)
+ p.Command[0], part.Msg.Envelope.Subject, part.Index)
doTerm(reader, name)
}
})
diff --git a/commands/msg/read.go b/commands/msg/read.go
index bac2ceb3..e55ed00e 100644
--- a/commands/msg/read.go
+++ b/commands/msg/read.go
@@ -2,16 +2,20 @@ package msg
import (
"fmt"
+ "strings"
"time"
- "git.sr.ht/~sircmpwn/getopt"
-
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/models"
"git.sr.ht/~rjarry/aerc/worker/types"
)
-type FlagMsg struct{}
+type FlagMsg struct {
+ Toggle bool `opt:"-t"`
+ Answered bool `opt:"-a" aliases:"flag,unflag"`
+ Flag models.Flags `opt:"-x" aliases:"flag,unflag" action:"ParseFlag"`
+ FlagName string
+}
func init() {
register(FlagMsg{})
@@ -25,6 +29,23 @@ func (FlagMsg) Complete(args []string) []string {
return nil
}
+func (f *FlagMsg) ParseFlag(arg string) error {
+ switch strings.ToLower(arg) {
+ case "seen":
+ f.Flag = models.SeenFlag
+ f.FlagName = "seen"
+ case "answered":
+ f.Flag = models.AnsweredFlag
+ f.FlagName = "answered"
+ case "flagged":
+ f.Flag = models.FlaggedFlag
+ f.FlagName = "flagged"
+ default:
+ return fmt.Errorf("Unknown flag %q", arg)
+ }
+ return nil
+}
+
// 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
@@ -32,85 +53,20 @@ func (FlagMsg) Complete(args []string) []string {
//
// If this was called as 'read' or 'unread', it has the same effect as
// 'flag' or 'unflag', respectively, but the 'Seen' flag is affected.
-func (FlagMsg) Execute(args []string) error {
- // The flag to change
- var flag models.Flags
- // User-readable name of the flag to change
- var flagName string
- // Whether to toggle the flag (true) or to enable/disable it (false)
- var toggle bool
- // Whether to enable (true) or disable (false) the flag
- enable := (args[0] == "read" || args[0] == "flag")
+func (f FlagMsg) Execute(args []string) error {
// User-readable name for the action being performed
var actionName string
- // Getopt option string, varies by command name
- var getoptString string
- // Help message to provide on parsing failure
- var helpMessage string
- // Used during parsing to prevent choosing a flag muliple times
- // A default flag will be used if this is false
- flagChosen := false
-
- if args[0] == "read" || args[0] == "unread" {
- flag = models.SeenFlag
- flagName = "read"
- getoptString = "t"
- helpMessage = "Usage: " + args[0] + " [-t]"
- } else { // 'flag' / 'unflag'
- flag = models.FlaggedFlag
- flagName = "flagged"
- getoptString = "tax:"
- helpMessage = "Usage: " + args[0] + " [-t] [-a | -x <flag>]"
- }
- opts, optind, err := getopt.Getopts(args, getoptString)
- if err != nil {
- return err
- }
- for _, opt := range opts {
- switch opt.Option {
- case 't':
- toggle = true
- case 'a':
- if flagChosen {
- return fmt.Errorf("Cannot choose a flag multiple times! " + helpMessage)
- }
- flag = models.AnsweredFlag
- flagName = "answered"
- flagChosen = true
- case 'x':
- if flagChosen {
- return fmt.Errorf("Cannot choose a flag multiple times! " + helpMessage)
- }
- // TODO: Support all flags?
- switch opt.Value {
- case "Seen":
- flag = models.SeenFlag
- flagName = "seen"
- case "Answered":
- flag = models.AnsweredFlag
- flagName = "answered"
- case "Flagged":
- flag = models.FlaggedFlag
- flagName = "flagged"
- default:
- return fmt.Errorf("Unknown / Prohibited flag \"%v\"", opt.Value)
- }
- flagChosen = true
+ switch args[0] {
+ case "read", "unread":
+ f.Flag = models.SeenFlag
+ f.FlagName = "seen"
+ case "flag", "unflag":
+ if f.Flag == 0 {
+ f.Flag = models.FlaggedFlag
+ f.FlagName = "flagged"
}
}
- switch {
- case toggle:
- actionName = "Toggling"
- case enable:
- actionName = "Setting"
- default:
- actionName = "Unsetting"
- }
- if optind != len(args) {
- // Any non-option arguments: Error
- return fmt.Errorf(helpMessage)
- }
h := newHelper()
store, err := h.store()
@@ -122,7 +78,7 @@ func (FlagMsg) Execute(args []string) error {
var toEnable []uint32
var toDisable []uint32
- if toggle {
+ if f.Toggle {
// If toggling, split messages into those that need to
// be enabled / disabled.
msgs, err := h.messages()
@@ -130,29 +86,35 @@ func (FlagMsg) Execute(args []string) error {
return err
}
for _, m := range msgs {
- if m.Flags.Has(flag) {
+ if m.Flags.Has(f.Flag) {
toDisable = append(toDisable, m.Uid)
} else {
toEnable = append(toEnable, m.Uid)
}
}
+ actionName = "Toggling"
} else {
msgUids, err := h.markedOrSelectedUids()
if err != nil {
return err
}
- if enable {
+ switch args[0] {
+ case "read", "flag":
toEnable = msgUids
- } else {
+ actionName = "Setting"
+ default:
toDisable = msgUids
+ actionName = "Unsetting"
}
}
+ status := fmt.Sprintf("%s flag %q successful", actionName, f.FlagName)
+
if len(toEnable) != 0 {
- store.Flag(toEnable, flag, true, func(msg types.WorkerMessage) {
+ store.Flag(toEnable, f.Flag, true, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
- app.PushStatus(actionName+" flag '"+flagName+"' successful", 10*time.Second)
+ app.PushStatus(status, 10*time.Second)
store.Marker().ClearVisualMark()
case *types.Error:
app.PushError(msg.Error.Error())
@@ -160,10 +122,10 @@ func (FlagMsg) Execute(args []string) error {
})
}
if len(toDisable) != 0 {
- store.Flag(toDisable, flag, false, func(msg types.WorkerMessage) {
+ store.Flag(toDisable, f.Flag, false, func(msg types.WorkerMessage) {
switch msg := msg.(type) {
case *types.Done:
- app.PushStatus(actionName+" flag '"+flagName+"' successful", 10*time.Second)
+ app.PushStatus(status, 10*time.Second)
store.Marker().ClearVisualMark()
case *types.Error:
app.PushError(msg.Error.Error())
diff --git a/commands/msg/recall.go b/commands/msg/recall.go
index 2d999b1d..3b78a763 100644
--- a/commands/msg/recall.go
+++ b/commands/msg/recall.go
@@ -15,10 +15,13 @@ import (
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/log"
"git.sr.ht/~rjarry/aerc/worker/types"
- "git.sr.ht/~sircmpwn/getopt"
)
-type Recall struct{}
+type Recall struct {
+ Force bool `opt:"-f"`
+ Edit bool `opt:"-e"`
+ NoEdit bool `opt:"-E"`
+}
func init() {
register(Recall{})
@@ -32,34 +35,15 @@ func (Recall) Complete(args []string) []string {
return nil
}
-func (Recall) Execute(args []string) error {
- force := false
- editHeaders := config.Compose.EditHeaders
-
- opts, optind, err := getopt.Getopts(args, "feE")
- if err != nil {
- return err
- }
- for _, opt := range opts {
- switch opt.Option {
- case 'f':
- force = true
- case 'e':
- editHeaders = true
- case 'E':
- editHeaders = false
- }
- }
- if len(args) != optind {
- return errors.New("Usage: recall [-f] [-e|-E]")
- }
+func (r Recall) Execute(args []string) error {
+ editHeaders := (config.Compose.EditHeaders || r.Edit) && !r.NoEdit
widget := app.SelectedTabContent().(app.ProvidesMessage)
acct := widget.SelectedAccount()
if acct == nil {
return errors.New("No account selected")
}
- if acct.SelectedDirectory() != acct.AccountConfig().Postpone && !force {
+ if acct.SelectedDirectory() != acct.AccountConfig().Postpone && !r.Force {
return errors.New("Use -f to recall from outside the " +
acct.AccountConfig().Postpone + " directory.")
}
@@ -164,7 +148,7 @@ func (Recall) Execute(args []string) error {
})
}
- if force {
+ if r.Force {
composer.SetRecalledFrom(acct.SelectedDirectory())
}
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index fff90fff..2ab9e9f8 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -9,8 +9,6 @@ import (
"strings"
"time"
- "git.sr.ht/~sircmpwn/getopt"
-
"git.sr.ht/~rjarry/aerc/app"
"git.sr.ht/~rjarry/aerc/commands/account"
"git.sr.ht/~rjarry/aerc/config"
@@ -24,7 +22,14 @@ import (
"github.com/emersion/go-message/mail"
)
-type reply struct{}
+type reply struct {
+ All bool `opt:"-a"`
+ Close bool `opt:"-c"`
+ Quote bool `opt:"-q"`
+ Template string `opt:"-T"`
+ Edit bool `opt:"-e"`
+ NoEdit bool `opt:"-E"`
+}
func init() {
register(reply{})
@@ -38,37 +43,8 @@ func (reply) Complete(args []string) []string {
return nil
}
-func (reply) Execute(args []string) error {
- opts, optind, err := getopt.Getopts(args, "acqT:eE")
- if err != nil {
- return err
- }
- if optind != len(args) {
- return errors.New("Usage: reply [-acq -T <template>] [-e|-E]")
- }
- var (
- quote bool
- replyAll bool
- closeOnReply bool
- template string
- )
- editHeaders := config.Compose.EditHeaders
- for _, opt := range opts {
- switch opt.Option {
- case 'a':
- replyAll = true
- case 'c':
- closeOnReply = true
- case 'q':
- quote = true
- case 'T':
- template = opt.Value
- case 'e':
- editHeaders = true
- case 'E':
- editHeaders = false
- }
- }
+func (r reply) Execute(args []string) error {
+ editHeaders := (config.Compose.EditHeaders || r.Edit) && !r.NoEdit
widget := app.SelectedTabContent().(app.ProvidesMessage)
acct := widget.SelectedAccount()
@@ -116,7 +92,7 @@ func (reply) Execute(args []string) error {
recSet.AddList(to)
- if replyAll {
+ if r.All {
// order matters, due to the deduping
// in order of importance, first parse the To, then the Cc header
@@ -165,12 +141,12 @@ func (reply) Execute(args []string) error {
addTab := func() error {
composer, err := app.NewComposer(acct,
acct.AccountConfig(), acct.Worker(), editHeaders,
- template, h, &original, nil)
+ r.Template, h, &original, nil)
if err != nil {
app.PushError("Error: " + err.Error())
return err
}
- if mv != nil && closeOnReply {
+ if mv != nil && r.Close {
app.RemoveTab(mv, true)
}
@@ -190,18 +166,19 @@ func (reply) Execute(args []string) error {
}
case c.Sent():
store.Answered([]uint32{msg.Uid}, true, nil)
- case mv != nil && closeOnReply:
+ case mv != nil && r.Close:
+ view := account.ViewMessage{Peek: true}
//nolint:errcheck // who cares?
- account.ViewMessage{}.Execute([]string{"-p"})
+ view.Execute([]string{"view", "-p"})
}
})
return nil
}
- if quote {
- if template == "" {
- template = config.Templates.QuotedReply
+ if r.Quote {
+ if r.Template == "" {
+ r.Template = config.Templates.QuotedReply
}
if crypto.IsEncrypted(msg.BodyStructure) {
@@ -256,8 +233,8 @@ func (reply) Execute(args []string) error {
})
return nil
} else {
- if template == "" {
- template = config.Templates.NewMessage
+ if r.Template == "" {
+ r.Template = config.Templates.NewMessage
}
return addTab()
}
diff --git a/commands/msg/toggle-thread-context.go b/commands/msg/toggle-thread-context.go
index 7530bc9e..0ef6778f 100644
--- a/commands/msg/toggle-thread-context.go
+++ b/commands/msg/toggle-thread-context.go
@@ -1,8 +1,6 @@
package msg
import (
- "errors"
-
"git.sr.ht/~rjarry/aerc/lib/ui"
)
@@ -21,9 +19,6 @@ func (ToggleThreadContext) Complete(args []string) []string {
}
func (ToggleThreadContext) Execute(args []string) error {
- if len(args) != 1 {
- return errors.New("Usage: toggle-entire-thread")
- }
h := newHelper()
store, err := h.store()
if err != nil {
diff --git a/commands/msg/toggle-threads.go b/commands/msg/toggle-threads.go
index 1d38685a..88cc763f 100644
--- a/commands/msg/toggle-threads.go
+++ b/commands/msg/toggle-threads.go
@@ -1,8 +1,6 @@
package msg
import (
- "errors"
-
"git.sr.ht/~rjarry/aerc/lib/state"
"git.sr.ht/~rjarry/aerc/lib/ui"
)
@@ -22,9 +20,6 @@ func (ToggleThreads) Complete(args []string) []string {
}
func (ToggleThreads) Execute(args []string) error {
- if len(args) != 1 {
- return errors.New("Usage: toggle-threads")
- }
h := newHelper()
acct, err := h.account()
if err != nil {
diff --git a/commands/msg/unsubscribe.go b/commands/msg/unsubscribe.go
index 069aedab..a489b3b9 100644
--- a/commands/msg/unsubscribe.go
+++ b/commands/msg/unsubscribe.go
@@ -12,13 +12,15 @@ import (
"git.sr.ht/~rjarry/aerc/config"
"git.sr.ht/~rjarry/aerc/lib"
"git.sr.ht/~rjarry/aerc/log"
- "git.sr.ht/~sircmpwn/getopt"
"github.com/emersion/go-message/mail"
)
// Unsubscribe helps people unsubscribe from mailing lists by way of the
// List-Unsubscribe header.
-type Unsubscribe struct{}
+type Unsubscribe struct {
+ Edit bool `opt:"-e"`
+ NoEdit bool `opt:"-E"`
+}
func init() {
register(Unsubscribe{})
@@ -35,23 +37,9 @@ func (Unsubscribe) Complete(args []string) []string {
}
// Execute runs the Unsubscribe command
-func (Unsubscribe) Execute(args []string) error {
- editHeaders := config.Compose.EditHeaders
- opts, optind, err := getopt.Getopts(args, "eE")
- if err != nil {
- return err
- }
- if len(args) != optind {
- return errors.New("Usage: unsubscribe [-e|-E]")
- }
- for _, opt := range opts {
- switch opt.Option {
- case 'e':
- editHeaders = true
- case 'E':
- editHeaders = false
- }
- }
+func (u Unsubscribe) Execute(args []string) error {
+ editHeaders := (config.Compose.EditHeaders || u.Edit) && !u.NoEdit
+
widget := app.SelectedTabContent().(app.ProvidesMessage)
msg, err := widget.SelectedMessage()
if err != nil {