viewer: add key passthrough mode

When trying to search in less, keys bound to viewer functions can't be
used as part of the search query, which makes the search useless.

Add a view::passthrough binding mode and a :toggle-key-passthrough
command go toggle in and out of that mode. By default, typing '/' in the
viewer is bound to enabling key passthrough and automatically inserting
'/', to easily enter "less" search mode. When in the passthrough mode,
all bindings are ignored by default. The default binds.conf only defines
a single keybinding in that mode: <Esc> to get back out. $ex is bound to
<C-x> to allow typing colons.

Signed-off-by: Kt Programs <ktprograms@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
This commit is contained in:
kt programs 2022-03-14 11:03:34 +08:00 committed by Robin Jarry
parent 4bc43d2741
commit 74366d895d
6 changed files with 91 additions and 24 deletions

View file

@ -0,0 +1,35 @@
package msgview
import (
"errors"
"git.sr.ht/~rjarry/aerc/widgets"
)
type ToggleKeyPassthrough struct{}
func init() {
register(ToggleKeyPassthrough{})
}
func (ToggleKeyPassthrough) Aliases() []string {
return []string{"toggle-key-passthrough"}
}
func (ToggleKeyPassthrough) Complete(aerc *widgets.Aerc, args []string) []string {
return nil
}
func (ToggleKeyPassthrough) Execute(aerc *widgets.Aerc, args []string) error {
if len(args) != 1 {
return errors.New("Usage: toggle-key-passthrough")
}
mv, _ := aerc.SelectedTab().(*widgets.MessageViewer)
keyPassthroughEnabled := mv.ToggleKeyPassthrough()
if keyPassthroughEnabled {
aerc.SetExtraStatus("[passthrough]")
} else {
aerc.ClearExtraStatus()
}
return nil
}

View file

@ -56,6 +56,7 @@ N = :prev-result<Enter>
<Esc> = :clear<Enter> <Esc> = :clear<Enter>
[view] [view]
/ = :toggle-key-passthrough<Enter>/
q = :close<Enter> q = :close<Enter>
| = :pipe<space> | = :pipe<space>
D = :delete<Enter> D = :delete<Enter>
@ -74,6 +75,11 @@ H = :toggle-headers<Enter>
J = :next<Enter> J = :next<Enter>
K = :prev<Enter> K = :prev<Enter>
[view::passthrough]
$noinherit = true
$ex = <C-x>
<Esc> = :toggle-key-passthrough<Enter>
[compose] [compose]
# Keybindings used when the embedded terminal is not selected in the compose # Keybindings used when the embedded terminal is not selected in the compose
# view # view

View file

@ -105,14 +105,15 @@ type AccountConfig struct {
} }
type BindingConfig struct { type BindingConfig struct {
Global *KeyBindings Global *KeyBindings
AccountWizard *KeyBindings AccountWizard *KeyBindings
Compose *KeyBindings Compose *KeyBindings
ComposeEditor *KeyBindings ComposeEditor *KeyBindings
ComposeReview *KeyBindings ComposeReview *KeyBindings
MessageList *KeyBindings MessageList *KeyBindings
MessageView *KeyBindings MessageView *KeyBindings
Terminal *KeyBindings MessageViewPassthrough *KeyBindings
Terminal *KeyBindings
} }
type BindingConfigContext struct { type BindingConfigContext struct {
@ -143,6 +144,7 @@ type ViewerConfig struct {
ShowHeaders bool `ini:"show-headers"` ShowHeaders bool `ini:"show-headers"`
AlwaysShowMime bool `ini:"always-show-mime"` AlwaysShowMime bool `ini:"always-show-mime"`
HeaderLayout [][]string `ini:"-"` HeaderLayout [][]string `ini:"-"`
KeyPassthrough bool `ini:"-"`
} }
type TriggersConfig struct { type TriggersConfig struct {
@ -591,14 +593,15 @@ func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) {
file.NameMapper = mapName file.NameMapper = mapName
config := &AercConfig{ config := &AercConfig{
Bindings: BindingConfig{ Bindings: BindingConfig{
Global: NewKeyBindings(), Global: NewKeyBindings(),
AccountWizard: NewKeyBindings(), AccountWizard: NewKeyBindings(),
Compose: NewKeyBindings(), Compose: NewKeyBindings(),
ComposeEditor: NewKeyBindings(), ComposeEditor: NewKeyBindings(),
ComposeReview: NewKeyBindings(), ComposeReview: NewKeyBindings(),
MessageList: NewKeyBindings(), MessageList: NewKeyBindings(),
MessageView: NewKeyBindings(), MessageView: NewKeyBindings(),
Terminal: NewKeyBindings(), MessageViewPassthrough: NewKeyBindings(),
Terminal: NewKeyBindings(),
}, },
ContextualBinds: []BindingConfigContext{}, ContextualBinds: []BindingConfigContext{},
@ -703,13 +706,14 @@ func LoadConfigFromFile(root *string, logger *log.Logger) (*AercConfig, error) {
} }
baseGroups := map[string]**KeyBindings{ baseGroups := map[string]**KeyBindings{
"default": &config.Bindings.Global, "default": &config.Bindings.Global,
"compose": &config.Bindings.Compose, "compose": &config.Bindings.Compose,
"messages": &config.Bindings.MessageList, "messages": &config.Bindings.MessageList,
"terminal": &config.Bindings.Terminal, "terminal": &config.Bindings.Terminal,
"view": &config.Bindings.MessageView, "view": &config.Bindings.MessageView,
"compose::editor": &config.Bindings.ComposeEditor, "view::passthrough": &config.Bindings.MessageViewPassthrough,
"compose::review": &config.Bindings.ComposeReview, "compose::editor": &config.Bindings.ComposeEditor,
"compose::review": &config.Bindings.ComposeReview,
} }
// Base Bindings // Base Bindings

View file

@ -564,6 +564,10 @@ are:
*[view]* *[view]*
keybindings for the message viewer keybindings for the message viewer
*[view::passthrough]*
keybindings for the viewer, when in key passthrough mode
(toggled with :toggle-key-passthrough)
*[compose]* *[compose]*
keybindings for the message composer keybindings for the message composer

View file

@ -201,7 +201,12 @@ func (aerc *Aerc) getBindings() *config.KeyBindings {
return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.Compose, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "compose") return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.Compose, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "compose")
} }
case *MessageViewer: case *MessageViewer:
return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.MessageView, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "view") switch view.Bindings() {
case "view::passthrough":
return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.MessageViewPassthrough, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "view::passthrough")
default:
return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.MessageView, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "view")
}
case *Terminal: case *Terminal:
return aerc.conf.Bindings.Terminal return aerc.conf.Bindings.Terminal
default: default:

View file

@ -286,6 +286,11 @@ func (mv *MessageViewer) ToggleHeaders() {
switcher.Invalidate() switcher.Invalidate()
} }
func (mv *MessageViewer) ToggleKeyPassthrough() bool {
mv.conf.Viewer.KeyPassthrough = !mv.conf.Viewer.KeyPassthrough
return mv.conf.Viewer.KeyPassthrough
}
func (mv *MessageViewer) SelectedMessagePart() *PartInfo { func (mv *MessageViewer) SelectedMessagePart() *PartInfo {
switcher := mv.switcher switcher := mv.switcher
part := switcher.parts[switcher.selected] part := switcher.parts[switcher.selected]
@ -325,6 +330,14 @@ func (mv *MessageViewer) NextPart() {
mv.Invalidate() mv.Invalidate()
} }
func (mv *MessageViewer) Bindings() string {
if mv.conf.Viewer.KeyPassthrough {
return "view::passthrough"
} else {
return "view"
}
}
func (mv *MessageViewer) Close() error { func (mv *MessageViewer) Close() error {
mv.switcher.Cleanup() mv.switcher.Cleanup()
return nil return nil