~sircmpwn/aerc

Abort composer on editor non-zero exit status v1 PROPOSED

Can you introduce this "Observer" idea in a separate commit?
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~sircmpwn/aerc/%3C20190814033806.20073-1-bridges2%40gmail.com%3E/mbox | git am -3
Learn more about email & git

[PATCH] Abort composer on editor non-zero exit status Export this patch

Daniel Bridges
---
I talked to Kevin about this, he wasn't going to be able to work on it
in the near term, and I had already done some work duplicate work
without realizing it. As always, open to suggestions.

 commands/account/compose.go | 10 +---------
 commands/compose/abort.go   |  1 -
 commands/msg/reply.go       | 10 +---------
 commands/msg/unsubscribe.go | 10 +---------
 lib/observer.go             | 27 +++++++++++++++++++++++++++
 widgets/aerc.go             | 26 +++++++++++++++++---------
 widgets/compose.go          | 17 +++++++++++++++++
 widgets/terminal.go         |  4 ++++
 8 files changed, 68 insertions(+), 37 deletions(-)
 create mode 100644 lib/observer.go

diff --git a/commands/account/compose.go b/commands/account/compose.go
index a4836b7..ee7ec16 100644
--- a/commands/account/compose.go
+++ b/commands/account/compose.go
@@ -31,15 +31,7 @@ func (_ Compose) Execute(aerc *widgets.Aerc, args []string) error {
 	acct := aerc.SelectedAccount()
 	composer := widgets.NewComposer(
 		aerc.Config(), acct.AccountConfig(), acct.Worker(), nil)
-	tab := aerc.NewTab(composer, "New email")
-	composer.OnHeaderChange("Subject", func(subject string) {
-		if subject == "" {
-			tab.Name = "New email"
-		} else {
-			tab.Name = subject
-		}
-		tab.Content.Invalidate()
-	})
+	aerc.NewComposerTab(composer, "New email")
 	go composer.SetContents(strings.NewReader(body))
 	return nil
 }
diff --git a/commands/compose/abort.go b/commands/compose/abort.go
index 4c121d7..f3ae113 100644
--- a/commands/compose/abort.go
+++ b/commands/compose/abort.go
@@ -26,7 +26,6 @@ func (_ Abort) Execute(aerc *widgets.Aerc, args []string) error {
 	}
 	composer, _ := aerc.SelectedTab().(*widgets.Composer)
 
-	aerc.RemoveTab(composer)
 	composer.Close()
 
 	return nil
diff --git a/commands/msg/reply.go b/commands/msg/reply.go
index 029cb42..4aef0b7 100644
--- a/commands/msg/reply.go
+++ b/commands/msg/reply.go
@@ -128,15 +128,7 @@ func (_ reply) Execute(aerc *widgets.Aerc, args []string) error {
 	}
 
 	addTab := func() {
-		tab := aerc.NewTab(composer, subject)
-		composer.OnHeaderChange("Subject", func(subject string) {
-			if subject == "" {
-				tab.Name = "New email"
-			} else {
-				tab.Name = subject
-			}
-			tab.Content.Invalidate()
-		})
+		aerc.NewComposerTab(composer, subject)
 	}
 
 	if args[0] == "forward" {
diff --git a/commands/msg/unsubscribe.go b/commands/msg/unsubscribe.go
index f18da07..50e1889 100644
--- a/commands/msg/unsubscribe.go
+++ b/commands/msg/unsubscribe.go
@@ -94,15 +94,7 @@ func unsubscribeMailto(aerc *widgets.Aerc, u *url.URL) error {
 		defaults,
 	)
 	composer.SetContents(strings.NewReader(u.Query().Get("body")))
-	tab := aerc.NewTab(composer, "unsubscribe")
-	composer.OnHeaderChange("Subject", func(subject string) {
-		if subject == "" {
-			tab.Name = "unsubscribe"
-		} else {
-			tab.Name = subject
-		}
-		tab.Content.Invalidate()
-	})
+	aerc.NewComposerTab(composer, "unsubscribe")
 	return nil
 }
 
diff --git a/lib/observer.go b/lib/observer.go
new file mode 100644
index 0000000..685f2e5
--- /dev/null
+++ b/lib/observer.go
@@ -0,0 +1,27 @@
+package lib
+
+type Observer struct {
+	callbacks map[int][]func()
+}
+
+func NewObserver() *Observer {
+	return &Observer{
+		callbacks: make(map[int][]func()),
+	}
+}
+
+func (o *Observer) Register(eventType int, fn func()) {
+	if cbs, ok := o.callbacks[eventType]; ok {
+		o.callbacks[eventType] = append(cbs, fn)
+	} else {
+		o.callbacks[eventType] = []func(){fn}
+	}
+}
+
+func (o *Observer) Emit(eventType int) {
+	if cbs, ok := o.callbacks[eventType]; ok {
+		for _, cb := range cbs {
+			cb()
+		}
+	}
+}
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 5a7914a..9b38a12 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -250,6 +250,22 @@ func (aerc *Aerc) NewTab(drawable ui.Drawable, name string) *ui.Tab {
 	return tab
 }
 
+func (aerc *Aerc) NewComposerTab(composer *Composer, title string) *ui.Tab {
+	tab := aerc.NewTab(composer, title)
+	composer.OnHeaderChange("Subject", func(subject string) {
+		if subject == "" {
+			tab.Name = title
+		} else {
+			tab.Name = subject
+		}
+		tab.Content.Invalidate()
+	})
+	composer.OnClose(func() {
+		aerc.RemoveTab(composer)
+	})
+	return tab
+}
+
 func (aerc *Aerc) RemoveTab(tab ui.Drawable) {
 	aerc.tabs.Remove(tab)
 }
@@ -395,15 +411,7 @@ func (aerc *Aerc) Mailto(addr *url.URL) error {
 		title = subj
 		composer.FocusTerminal()
 	}
-	tab := aerc.NewTab(composer, title)
-	composer.OnHeaderChange("Subject", func(subject string) {
-		if subject == "" {
-			tab.Name = "New email"
-		} else {
-			tab.Name = subject
-		}
-		tab.Content.Invalidate()
-	})
+	aerc.NewComposerTab(composer, title)
 	return nil
 }
 
diff --git a/widgets/compose.go b/widgets/compose.go
index c7e38b8..310aadc 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -20,10 +20,15 @@ import (
 	"github.com/pkg/errors"
 
 	"git.sr.ht/~sircmpwn/aerc/config"
+	"git.sr.ht/~sircmpwn/aerc/lib"
 	"git.sr.ht/~sircmpwn/aerc/lib/ui"
 	"git.sr.ht/~sircmpwn/aerc/worker/types"
 )
 
+const (
+	ON_CLOSE = iota
+)
+
 type Composer struct {
 	editors map[string]*headerEditor
 
@@ -38,6 +43,7 @@ type Composer struct {
 	header      *ui.Grid
 	review      *reviewMessage
 	worker      *types.Worker
+	observer    *lib.Observer
 
 	layout    HeaderLayout
 	focusable []ui.DrawableInteractive
@@ -74,6 +80,7 @@ func NewComposer(conf *config.AercConfig,
 		// You have to backtab to get to "From", since you usually don't edit it
 		focused:   1,
 		focusable: focusable,
+		observer:  lib.NewObserver(),
 	}
 
 	c.updateGrid()
@@ -162,6 +169,10 @@ func (c *Composer) OnHeaderChange(header string, fn func(subject string)) {
 	}
 }
 
+func (c *Composer) OnClose(fn func()) {
+	c.observer.Register(ON_CLOSE, fn)
+}
+
 func (c *Composer) Draw(ctx *ui.Context) {
 	c.grid.Draw(ctx)
 }
@@ -187,6 +198,7 @@ func (c *Composer) Close() {
 		c.editor.Destroy()
 		c.editor = nil
 	}
+	c.observer.Emit(ON_CLOSE)
 }
 
 func (c *Composer) Bindings() string {
@@ -462,6 +474,11 @@ func (c *Composer) resetReview() {
 }
 
 func (c *Composer) termClosed(err error) {
+	if _, ok := err.(*exec.ExitError); ok {
+		// editor exit code not 0, aborting
+		c.Close()
+		return
+	}
 	c.grid.RemoveChild(c.editor)
 	c.review = newReviewMessage(c, err)
 	c.grid.AddChild(c.review).At(1, 0)
diff --git a/widgets/terminal.go b/widgets/terminal.go
index 008a36f..0b2fec0 100644
--- a/widgets/terminal.go
+++ b/widgets/terminal.go
@@ -180,6 +180,10 @@ func (term *Terminal) Close(err error) {
 	}
 	if term.cmd != nil && term.cmd.Process != nil {
 		term.cmd.Process.Kill()
+		if err == nil {
+			err = term.cmd.Wait()
+			term.err = err
+		}
 		term.cmd = nil
 	}
 	if !term.closed && term.OnClose != nil {
-- 
2.20.1
Can you introduce this "Observer" idea in a separate commit?
View this thread in the archives