diff options
author | Vitaly Ovchinnikov <v@postbox.nz> | 2023-09-29 18:48:47 +0000 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-10-22 14:56:27 +0200 |
commit | 14fc59f189775dbe95daf582c08c9c1b3cd949b2 (patch) | |
tree | 285a1b8bb0d4a27a0d04ebd09287d0cabd9654ce | |
parent | 6bbb477d441b17756f89e0034c26c4c3cdc941af (diff) | |
download | aerc-14fc59f189775dbe95daf582c08c9c1b3cd949b2.tar.gz |
terminal: add `:send-keys` command
Add a new command for sending keystrokes to the active terminal, if
there is one visible. Covers split preview, message viewer, composer and
the terminal mode.
This can be used to navigate the embedded applications to scroll or
safely quit them when needed.
Signed-off-by: Vitaly Ovchinnikov <v@postbox.nz>
Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r-- | CHANGELOG.md | 1 | ||||
-rw-r--r-- | app/account.go | 8 | ||||
-rw-r--r-- | app/compose.go | 4 | ||||
-rw-r--r-- | app/msgviewer.go | 18 | ||||
-rw-r--r-- | app/terminal.go | 8 | ||||
-rw-r--r-- | commands/send-keys.go | 51 | ||||
-rw-r--r-- | doc/aerc.1.scd | 17 |
7 files changed, 107 insertions, 0 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index df55324e..707409ca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Added - New `flagged` criteria for `:sort` +- New `:send-keys` command to control embedded terminals. ### Fixed diff --git a/app/account.go b/app/account.go index 312ba89b..817b9b74 100644 --- a/app/account.go +++ b/app/account.go @@ -220,6 +220,14 @@ func (acct *AccountView) SelectedMessagePart() *PartInfo { return nil } +func (acct *AccountView) Terminal() *Terminal { + if acct.split == nil { + return nil + } + + return acct.split.Terminal() +} + func (acct *AccountView) isSelected() bool { return acct == SelectedAccount() } diff --git a/app/compose.go b/app/compose.go index 546184d8..76f28881 100644 --- a/app/compose.go +++ b/app/compose.go @@ -769,6 +769,10 @@ func (c *Composer) OnClose(fn func(composer *Composer)) { c.onClose = append(c.onClose, fn) } +func (c *Composer) Terminal() *Terminal { + return c.editor +} + func (c *Composer) Draw(ctx *ui.Context) { c.setTitle() c.width = ctx.Width() diff --git a/app/msgviewer.go b/app/msgviewer.go index bb63b28d..8ac22bcc 100644 --- a/app/msgviewer.go +++ b/app/msgviewer.go @@ -276,6 +276,24 @@ func (mv *MessageViewer) Invalidate() { ui.Invalidate() } +func (mv *MessageViewer) Terminal() *Terminal { + if mv.switcher == nil { + return nil + } + + nparts := len(mv.switcher.parts) + if nparts == 0 || mv.switcher.selected < 0 || mv.switcher.selected >= nparts { + return nil + } + + pv := mv.switcher.parts[mv.switcher.selected] + if pv == nil { + return nil + } + + return pv.term +} + func (mv *MessageViewer) Store() *lib.MessageStore { return mv.msg.Store() } diff --git a/app/terminal.go b/app/terminal.go index 1b177f3e..85d84db6 100644 --- a/app/terminal.go +++ b/app/terminal.go @@ -12,6 +12,10 @@ import ( "github.com/gdamore/tcell/v2" ) +type HasTerminal interface { + Terminal() *Terminal +} + type Terminal struct { closed int32 cmd *exec.Cmd @@ -113,6 +117,10 @@ func (term *Terminal) Show(visible bool) { term.visible = visible } +func (term *Terminal) Terminal() *Terminal { + return term +} + func (term *Terminal) MouseEvent(localX int, localY int, event tcell.Event) { ev, ok := event.(*tcell.EventMouse) if !ok { diff --git a/commands/send-keys.go b/commands/send-keys.go new file mode 100644 index 00000000..f9cae392 --- /dev/null +++ b/commands/send-keys.go @@ -0,0 +1,51 @@ +package commands + +import ( + "strings" + + "git.sr.ht/~rjarry/aerc/app" + "git.sr.ht/~rjarry/aerc/config" + "github.com/gdamore/tcell/v2" + "github.com/pkg/errors" +) + +type SendKeys struct{} + +func init() { + register(SendKeys{}) +} + +func (SendKeys) Aliases() []string { + return []string{"send-keys"} +} + +func (SendKeys) Complete(args []string) []string { + return nil +} + +func (SendKeys) Execute(args []string) error { + tab, ok := app.SelectedTabContent().(app.HasTerminal) + if !ok { + return errors.New("There is no terminal here") + } + + term := tab.Terminal() + if term == nil { + return errors.New("The terminal is not active") + } + + text2send := strings.Join(args[1:], "") + keys2send, err := config.ParseKeyStrokes(text2send) + if err != nil { + return errors.Wrapf(err, "Unable to parse keystroke: '%s'", text2send) + } + + for _, key := range keys2send { + ev := tcell.NewEventKey(key.Key, key.Rune, key.Modifiers) + term.Event(ev) + } + + term.Invalidate() + + return nil +} diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd index e51ae8d1..a48735d7 100644 --- a/doc/aerc.1.scd +++ b/doc/aerc.1.scd @@ -119,6 +119,23 @@ These commands work in any context. *:pwd* Displays aerc's current working directory in the status bar. +*:send-keys* _<keystrokes>_ + Send keystrokes to the currently visible terminal, if any. Can be used to + control embedded editors to save drafts or quit in a safe manner. + + Here's an example of quiting a Vim-like editor: + + *:send-keys* _<Esc>:wq!<Enter>_ + + Note: when used in _binds.conf_ (see *aerc-binds*(5)), angle brackets + need to be escaped in order to make their way to the command: + + <C-q> = :send-keys \\<Esc\\>:wq!\\<Enter\\><Enter> + + This way the _<Esc>_ and the first _<Enter>_ keystrokes are passed to + *:send-keys*, while the last _<Enter>_ keystroke is executed directly, + committing the *:send-keys* command's execution. + *:term* [_<command>..._]++ *:terminal* Opens a new terminal tab with a shell running in the current working |