From f77d7c2c3d578ae5a724e1e8b91656304dde75d0 Mon Sep 17 00:00:00 2001 From: Drew DeVault Date: Tue, 14 May 2019 14:27:28 -0400 Subject: Add distinct keybindings for each compose view --- commands/compose/send-message.go | 163 --------------------------------------- commands/compose/send.go | 163 +++++++++++++++++++++++++++++++++++++++ config/bindings.go | 2 +- config/binds.conf | 13 ++++ config/config.go | 27 ++++--- widgets/aerc.go | 11 ++- widgets/compose.go | 10 +++ 7 files changed, 213 insertions(+), 176 deletions(-) delete mode 100644 commands/compose/send-message.go create mode 100644 commands/compose/send.go diff --git a/commands/compose/send-message.go b/commands/compose/send-message.go deleted file mode 100644 index 9b59e5e3..00000000 --- a/commands/compose/send-message.go +++ /dev/null @@ -1,163 +0,0 @@ -package compose - -import ( - "crypto/tls" - "errors" - "fmt" - "net/mail" - "net/url" - "strings" - "time" - - "github.com/emersion/go-sasl" - "github.com/emersion/go-smtp" - "github.com/gdamore/tcell" - - "git.sr.ht/~sircmpwn/aerc2/widgets" -) - -func init() { - register("send-message", SendMessage) -} - -func SendMessage(aerc *widgets.Aerc, args []string) error { - if len(args) > 1 { - return errors.New("Usage: send-message") - } - composer, _ := aerc.SelectedTab().(*widgets.Composer) - config := composer.Config() - - if config.Outgoing == "" { - return errors.New( - "No outgoing mail transport configured for this account") - } - - uri, err := url.Parse(config.Outgoing) - if err != nil { - return err - } - var ( - scheme string - auth string = "plain" - ) - parts := strings.Split(uri.Scheme, "+") - if len(parts) == 1 { - scheme = parts[0] - } else if len(parts) == 2 { - scheme = parts[0] - auth = parts[1] - } else { - return fmt.Errorf("Unknown transfer protocol %s", uri.Scheme) - } - - header, rcpts, err := composer.Header() - if err != nil { - return err - } - - if config.From == "" { - return errors.New("No 'From' configured for this account") - } - from, err := mail.ParseAddress(config.From) - if err != nil { - return err - } - - var ( - saslClient sasl.Client - conn *smtp.Client - ) - switch auth { - case "": - fallthrough - case "none": - saslClient = nil - case "plain": - password, _ := uri.User.Password() - saslClient = sasl.NewPlainClient("", uri.User.Username(), password) - default: - return fmt.Errorf("Unsupported auth mechanism %s", auth) - } - - aerc.SetStatus("Sending...") - - sendAsync := func() { - tlsConfig := &tls.Config{ - // TODO: ask user first - InsecureSkipVerify: true, - } - switch scheme { - case "smtp": - host := uri.Host - if !strings.ContainsRune(host, ':') { - host = host + ":587" // Default to submission port - } - conn, err = smtp.Dial(host) - if err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - defer conn.Close() - if sup, _ := conn.Extension("STARTTLS"); sup { - // TODO: let user configure tls? - if err = conn.StartTLS(tlsConfig); err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - } - case "smtps": - host := uri.Host - if !strings.ContainsRune(host, ':') { - host = host + ":465" // Default to smtps port - } - conn, err = smtp.DialTLS(host, tlsConfig) - if err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - defer conn.Close() - } - - // TODO: sendmail - if saslClient != nil { - if err = conn.Auth(saslClient); err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - } - // TODO: the user could conceivably want to use a different From and sender - if err = conn.Mail(from.Address); err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - for _, rcpt := range rcpts { - if err = conn.Rcpt(rcpt); err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - } - wc, err := conn.Data() - if err != nil { - aerc.PushStatus(" "+err.Error(), 10*time.Second). - Color(tcell.ColorDefault, tcell.ColorRed) - return - } - defer wc.Close() - composer.WriteMessage(header, wc) - composer.Close() - aerc.RemoveTab(composer) - } - - go func() { - sendAsync() - // TODO: Use a stack - aerc.SetStatus("Sent.") - }() - return nil -} diff --git a/commands/compose/send.go b/commands/compose/send.go new file mode 100644 index 00000000..ed3bd43d --- /dev/null +++ b/commands/compose/send.go @@ -0,0 +1,163 @@ +package compose + +import ( + "crypto/tls" + "errors" + "fmt" + "net/mail" + "net/url" + "strings" + "time" + + "github.com/emersion/go-sasl" + "github.com/emersion/go-smtp" + "github.com/gdamore/tcell" + + "git.sr.ht/~sircmpwn/aerc2/widgets" +) + +func init() { + register("send", SendMessage) +} + +func SendMessage(aerc *widgets.Aerc, args []string) error { + if len(args) > 1 { + return errors.New("Usage: send-message") + } + composer, _ := aerc.SelectedTab().(*widgets.Composer) + config := composer.Config() + + if config.Outgoing == "" { + return errors.New( + "No outgoing mail transport configured for this account") + } + + uri, err := url.Parse(config.Outgoing) + if err != nil { + return err + } + var ( + scheme string + auth string = "plain" + ) + parts := strings.Split(uri.Scheme, "+") + if len(parts) == 1 { + scheme = parts[0] + } else if len(parts) == 2 { + scheme = parts[0] + auth = parts[1] + } else { + return fmt.Errorf("Unknown transfer protocol %s", uri.Scheme) + } + + header, rcpts, err := composer.Header() + if err != nil { + return err + } + + if config.From == "" { + return errors.New("No 'From' configured for this account") + } + from, err := mail.ParseAddress(config.From) + if err != nil { + return err + } + + var ( + saslClient sasl.Client + conn *smtp.Client + ) + switch auth { + case "": + fallthrough + case "none": + saslClient = nil + case "plain": + password, _ := uri.User.Password() + saslClient = sasl.NewPlainClient("", uri.User.Username(), password) + default: + return fmt.Errorf("Unsupported auth mechanism %s", auth) + } + + aerc.SetStatus("Sending...") + + sendAsync := func() { + tlsConfig := &tls.Config{ + // TODO: ask user first + InsecureSkipVerify: true, + } + switch scheme { + case "smtp": + host := uri.Host + if !strings.ContainsRune(host, ':') { + host = host + ":587" // Default to submission port + } + conn, err = smtp.Dial(host) + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + defer conn.Close() + if sup, _ := conn.Extension("STARTTLS"); sup { + // TODO: let user configure tls? + if err = conn.StartTLS(tlsConfig); err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + } + case "smtps": + host := uri.Host + if !strings.ContainsRune(host, ':') { + host = host + ":465" // Default to smtps port + } + conn, err = smtp.DialTLS(host, tlsConfig) + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + defer conn.Close() + } + + // TODO: sendmail + if saslClient != nil { + if err = conn.Auth(saslClient); err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + } + // TODO: the user could conceivably want to use a different From and sender + if err = conn.Mail(from.Address); err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + for _, rcpt := range rcpts { + if err = conn.Rcpt(rcpt); err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + } + wc, err := conn.Data() + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorDefault, tcell.ColorRed) + return + } + defer wc.Close() + composer.WriteMessage(header, wc) + composer.Close() + aerc.RemoveTab(composer) + } + + go func() { + sendAsync() + // TODO: Use a stack + aerc.SetStatus("Sent.") + }() + return nil +} diff --git a/config/bindings.go b/config/bindings.go index 4cb48058..a9a57c57 100644 --- a/config/bindings.go +++ b/config/bindings.go @@ -283,7 +283,7 @@ func init() { keyNames["ACK"] = KeyStroke{tcell.KeyACK, 0} keyNames["BEL"] = KeyStroke{tcell.KeyBEL, 0} keyNames["BS"] = KeyStroke{tcell.KeyBS, 0} - keyNames["TAB"] = KeyStroke{tcell.KeyTAB, 0} + keyNames["tab"] = KeyStroke{tcell.KeyTAB, 0} keyNames["LF"] = KeyStroke{tcell.KeyLF, 0} keyNames["VT"] = KeyStroke{tcell.KeyVT, 0} keyNames["FF"] = KeyStroke{tcell.KeyFF, 0} diff --git a/config/binds.conf b/config/binds.conf index 1102c214..62004a95 100644 --- a/config/binds.conf +++ b/config/binds.conf @@ -40,6 +40,14 @@ a = :reply -a f = :forward [compose] +# Keybindings used when the embedded terminal is not selected in the compose +# view + = :prev-field + = :next-field + = :next-field + +[compose::editor] +# Keybindings used when the embedded terminal is selected in the compose view $noinherit = true $ex = = :prev-field @@ -47,6 +55,11 @@ $ex = = :prev-tab = :next-tab +[compose::review] +# Keybindings used when reviewing a message to be sent +y = :send +n = :abort + [terminal] $noinherit = true $ex = diff --git a/config/config.go b/config/config.go index 79d1810b..5c5094ab 100644 --- a/config/config.go +++ b/config/config.go @@ -39,11 +39,13 @@ type AccountConfig struct { } type BindingConfig struct { - Global *KeyBindings - Compose *KeyBindings - MessageList *KeyBindings - MessageView *KeyBindings - Terminal *KeyBindings + Global *KeyBindings + Compose *KeyBindings + ComposeEditor *KeyBindings + ComposeReview *KeyBindings + MessageList *KeyBindings + MessageView *KeyBindings + Terminal *KeyBindings } type FilterConfig struct { @@ -139,11 +141,13 @@ func LoadConfig(root *string) (*AercConfig, error) { file.NameMapper = mapName config := &AercConfig{ Bindings: BindingConfig{ - Global: NewKeyBindings(), - Compose: NewKeyBindings(), - MessageList: NewKeyBindings(), - MessageView: NewKeyBindings(), - Terminal: NewKeyBindings(), + Global: NewKeyBindings(), + Compose: NewKeyBindings(), + ComposeEditor: NewKeyBindings(), + ComposeReview: NewKeyBindings(), + MessageList: NewKeyBindings(), + MessageView: NewKeyBindings(), + Terminal: NewKeyBindings(), }, Ini: file, @@ -223,6 +227,9 @@ func LoadConfig(root *string) (*AercConfig, error) { "messages": &config.Bindings.MessageList, "terminal": &config.Bindings.Terminal, "view": &config.Bindings.MessageView, + + "compose::editor": &config.Bindings.ComposeEditor, + "compose::review": &config.Bindings.ComposeReview, } for _, name := range binds.SectionStrings() { sec, err := binds.GetSection(name) diff --git a/widgets/aerc.go b/widgets/aerc.go index fb109d42..61d46457 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -88,11 +88,18 @@ func (aerc *Aerc) Draw(ctx *libui.Context) { } func (aerc *Aerc) getBindings() *config.KeyBindings { - switch aerc.SelectedTab().(type) { + switch view := aerc.SelectedTab().(type) { case *AccountView: return aerc.conf.Bindings.MessageList case *Composer: - return aerc.conf.Bindings.Compose + switch view.Bindings() { + case "compose::editor": + return aerc.conf.Bindings.ComposeEditor + case "compose::review": + return aerc.conf.Bindings.ComposeReview + default: + return aerc.conf.Bindings.Compose + } case *MessageViewer: return aerc.conf.Bindings.MessageView case *Terminal: diff --git a/widgets/compose.go b/widgets/compose.go index 38c33fcd..2daa29c2 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -120,6 +120,16 @@ func (c *Composer) Close() { } } +func (c *Composer) Bindings() string { + if c.editor == nil { + return "compose::review" + } else if c.editor == c.focusable[c.focused] { + return "compose::editor" + } else { + return "compose" + } +} + func (c *Composer) Event(event tcell.Event) bool { return c.focusable[c.focused].Event(event) } -- cgit