aerc

Working clone of aerc-mail.org
git clone git://git.danielmoch.com/aerc.git
Log | Files | Refs | README | LICENSE

commit 700dea23fa75f213af2f99449db994008eab9d21
parent 4465646fedc5dd3efa680a7cc8d06671350b75b9
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 30 Mar 2019 11:58:24 -0400

Implement :pipe

Diffstat:
Acommands/account/pipe.go | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcommands/terminal/close.go | 1+
Mgo.mod | 2+-
Mgo.sum | 2++
Mwidgets/terminal.go | 20++++++++++++++------
5 files changed, 91 insertions(+), 7 deletions(-)

diff --git a/commands/account/pipe.go b/commands/account/pipe.go @@ -0,0 +1,73 @@ +package account + +import ( + "bytes" + "errors" + "io" + "os/exec" + "time" + + "git.sr.ht/~sircmpwn/aerc2/widgets" + + "github.com/gdamore/tcell" + "github.com/mohamedattahri/mail" +) + +func init() { + register("pipe", Pipe) +} + +func Pipe(aerc *widgets.Aerc, args []string) error { + if len(args) < 2 { + return errors.New("Usage: :pipe <cmd> [args...]") + } + acct := aerc.SelectedAccount() + if acct == nil { + return errors.New("No account selected") + } + store := acct.Messages().Store() + msg := acct.Messages().Selected() + store.FetchBodies([]uint32{msg.Uid}, func(msg *mail.Message) { + cmd := exec.Command(args[1], args[2:]...) + pipe, err := cmd.StdinPipe() + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorRed, tcell.ColorWhite) + return + } + term, err := widgets.NewTerminal(cmd) + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorRed, tcell.ColorWhite) + return + } + host := widgets.NewTermHost(term, aerc.Config()) + name := msg.Subject() + if len(name) > 12 { + name = name[:12] + } + aerc.NewTab(host, args[1] + " <" + name) + term.OnClose = func(err error) { + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorRed, tcell.ColorWhite) + } else { + // TODO: Tab-specific status stacks + aerc.PushStatus("Process complete, press any key to close.", + 10*time.Second) + } + } + term.OnStart = func() { + go func() { + reader := bytes.NewBuffer(msg.Bytes()) + _, err := io.Copy(pipe, reader) + if err != nil { + aerc.PushStatus(" "+err.Error(), 10*time.Second). + Color(tcell.ColorRed, tcell.ColorWhite) + } + pipe.Close() + }() + } + }) + return nil +} diff --git a/commands/terminal/close.go b/commands/terminal/close.go @@ -19,5 +19,6 @@ func CommandClose(aerc *widgets.Aerc, args []string) error { return errors.New("Error: not a terminal") } thost.Terminal().Close(nil) + aerc.RemoveTab(thost) return nil } diff --git a/go.mod b/go.mod @@ -2,6 +2,7 @@ module git.sr.ht/~sircmpwn/aerc2 require ( git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190322002230-17c9f17a421a + git.sr.ht/~sircmpwn/pty v0.0.0-20190330154901-3a43678975a9 github.com/emersion/go-imap v1.0.0-beta.1 github.com/emersion/go-imap-idle v0.0.0-20180114101550-2af93776db6b github.com/emersion/go-sasl v0.0.0-20161116183048-7e096a0a6197 // indirect @@ -9,7 +10,6 @@ require ( github.com/gdamore/tcell v1.0.0 github.com/go-ini/ini v1.42.0 github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf - github.com/kr/pty v1.1.3 github.com/kyoh86/xdg v0.0.0-20171127140545-8db68a8ea76a github.com/lucasb-eyer/go-colorful v0.0.0-20180531031333-d9cec903b20c github.com/mattn/go-isatty v0.0.3 diff --git a/go.sum b/go.sum @@ -20,6 +20,8 @@ git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190322001004-741298f37f76 h1:D9PCVZ+pgt git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190322001004-741298f37f76/go.mod h1:hT88+cTemwwESbMptwC7O33qrJfQX0SgRWbXlndUS2c= git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190322002230-17c9f17a421a h1:ktjo0NVokhdhhyS/VYA1/8R/Az8x5x43r0SuI6McqW4= git.sr.ht/~sircmpwn/go-libvterm v0.0.0-20190322002230-17c9f17a421a/go.mod h1:hT88+cTemwwESbMptwC7O33qrJfQX0SgRWbXlndUS2c= +git.sr.ht/~sircmpwn/pty v0.0.0-20190330154901-3a43678975a9 h1:WWPN5lf6KzXp3xWRrPQZ4MLR3yrFEI4Ysz7HSQ1G/yo= +git.sr.ht/~sircmpwn/pty v0.0.0-20190330154901-3a43678975a9/go.mod h1:8Jmcax8M9nYoEwBhVBhv2ixLRCoUqlbQPE95VpPu43I= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/emersion/go-imap v1.0.0-beta.1 h1:bTCaVlUnb5mKoW9lEukusxguSYYZPer+q0g5t+vw5X0= diff --git a/widgets/terminal.go b/widgets/terminal.go @@ -9,8 +9,8 @@ import ( "git.sr.ht/~sircmpwn/aerc2/lib/ui" "git.sr.ht/~sircmpwn/go-libvterm" + "git.sr.ht/~sircmpwn/pty" "github.com/gdamore/tcell" - "github.com/kr/pty" ) type vtermKey struct { @@ -105,6 +105,7 @@ type Terminal struct { vterm *vterm.VTerm OnClose func(err error) + OnStart func() OnTitle func(title string) } @@ -192,6 +193,9 @@ func (term *Terminal) flushTerminal() { } func (term *Terminal) Close(err error) { + if term.closed { + return + } term.mutex.Lock() defer term.mutex.Unlock() term.err = err @@ -226,11 +230,6 @@ func (term *Terminal) Invalidate() { func (term *Terminal) Draw(ctx *ui.Context) { if term.closed { - if term.err != nil { - ui.NewText(term.err.Error()).Strategy(ui.TEXT_CENTER).Draw(ctx) - } else { - ui.NewText("Terminal closed").Strategy(ui.TEXT_CENTER).Draw(ctx) - } return } @@ -252,6 +251,9 @@ func (term *Terminal) Draw(ctx *ui.Context) { return } term.start <- nil + if term.OnStart != nil { + term.OnStart() + } } term.ctx = ctx // gross @@ -309,6 +311,9 @@ func (term *Terminal) Draw(ctx *ui.Context) { } func (term *Terminal) Focus(focus bool) { + if term.closed { + return + } term.focus = focus if term.ctx != nil { if !term.focus { @@ -339,6 +344,9 @@ func convertMods(mods tcell.ModMask) vterm.Modifier { } func (term *Terminal) Event(event tcell.Event) bool { + if term.closed { + return false + } switch event := event.(type) { case *tcell.EventKey: if event.Key() == tcell.KeyRune {