diff --git a/config/bindings.go b/config/bindings.go index 1882f74..0032d72 100644 --- a/config/bindings.go +++ b/config/bindings.go @@ -44,6 +44,16 @@ func NewKeyBindings() *KeyBindings { } } +func MergeBindings(bindings ...*KeyBindings) *KeyBindings { + merged := NewKeyBindings() + for _, b := range bindings { + merged.bindings = append(merged.bindings, b.bindings...) + } + merged.ExKey = bindings[0].ExKey + merged.Globals = bindings[0].Globals + return merged +} + func (bindings *KeyBindings) Add(binding *Binding) { // TODO: Search for conflicts? bindings.bindings = append(bindings.bindings, binding) diff --git a/config/binds.conf b/config/binds.conf new file mode 100644 index 0000000..814f9f5 --- /dev/null +++ b/config/binds.conf @@ -0,0 +1,32 @@ +# Binds are of the form = +# To use '=' in a key sequence, substitute it with "Eq": "" +# If you wish to bind #, you can wrap the key sequence in quotes: "#" = quit +q = :quit +L = :next-tab +H = :prev-tab + = :term + +[messages] +j = :next-message + = :next-message + = :next-message 50% + = :next-message 100% + = :next-message -s 100% + +k = :prev-message + = :prev-message + = :prev-message 50% + = :prev-message 100% + = :prev-message -s 100% +g = :select-message 0 +G = :select-message -1 + +J = :next-folder +K = :prev-folder + + = :view-message +d = :confirm 'Really delete this message?' ':delete-message' +D = :delete-message + +c = :cf +$ = :term diff --git a/config/config.go b/config/config.go index 537626f..7aff1ea 100644 --- a/config/config.go +++ b/config/config.go @@ -1,6 +1,7 @@ package config import ( + "errors" "fmt" "path" "strings" @@ -29,8 +30,16 @@ type AccountConfig struct { Params map[string]string } +type BindingConfig struct { + Global *KeyBindings + Compose *KeyBindings + MessageList *KeyBindings + MessageView *KeyBindings + Terminal *KeyBindings +} + type AercConfig struct { - Lbinds *KeyBindings + Bindings BindingConfig Ini *ini.File `ini:"-"` Accounts []AccountConfig `ini:"-"` Ui UIConfig @@ -98,8 +107,14 @@ func LoadConfig(root *string) (*AercConfig, error) { } file.NameMapper = mapName config := &AercConfig{ - Lbinds: NewKeyBindings(), - Ini: file, + Bindings: BindingConfig{ + Global: NewKeyBindings(), + Compose: NewKeyBindings(), + MessageList: NewKeyBindings(), + MessageView: NewKeyBindings(), + Terminal: NewKeyBindings(), + }, + Ini: file, Ui: UIConfig{ IndexFormat: "%4C %Z %D %-17.17n %s", @@ -121,20 +136,65 @@ func LoadConfig(root *string) (*AercConfig, error) { return nil, err } } - if lbinds, err := file.GetSection("lbinds"); err == nil { - for key, value := range lbinds.KeysHash() { - binding, err := ParseBinding(key, value) - if err != nil { - return nil, err - } - config.Lbinds.Add(binding) - } - } accountsPath := path.Join(*root, "accounts.conf") if accounts, err := loadAccountConfig(accountsPath); err != nil { return nil, err } else { config.Accounts = accounts } + binds, err := ini.Load(path.Join(*root, "binds.conf")) + if err != nil { + return nil, err + } + groups := map[string]**KeyBindings{ + "default": &config.Bindings.Global, + "compose": &config.Bindings.Compose, + "messages": &config.Bindings.MessageList, + "terminal": &config.Bindings.Terminal, + "view": &config.Bindings.MessageView, + } + for _, name := range binds.SectionStrings() { + sec, err := binds.GetSection(name) + if err != nil { + return nil, err + } + group, ok := groups[strings.ToLower(name)] + if !ok { + return nil, errors.New("Unknown keybinding group " + name) + } + bindings := NewKeyBindings() + for key, value := range sec.KeysHash() { + if key == "$ex" { + strokes, err := ParseKeyStrokes(value) + if err != nil { + return nil, err + } + if len(strokes) != 1 { + return nil, errors.New( + "Error: only one keystroke supported for $ex") + } + bindings.ExKey = strokes[0] + continue + } + if key == "$noinherit" { + if value == "false" { + continue + } + if value != "true" { + return nil, errors.New( + "Error: expected 'true' or 'false' for $noinherit") + } + bindings.Globals = false + } + binding, err := ParseBinding(key, value) + if err != nil { + return nil, err + } + bindings.Add(binding) + } + *group = MergeBindings(bindings, *group) + } + // Globals can't inherit from themselves + config.Bindings.Global.Globals = false return config, nil } diff --git a/widgets/aerc.go b/widgets/aerc.go index af2d0df..8d456b1 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -94,6 +94,24 @@ func (aerc *Aerc) Draw(ctx *libui.Context) { aerc.grid.Draw(ctx) } +func (aerc *Aerc) getBindings() *config.KeyBindings { + switch aerc.SelectedTab().(type) { + case *AccountView: + return aerc.conf.Bindings.MessageList + default: + return aerc.conf.Bindings.Global + } +} + +func (aerc *Aerc) simulate(strokes []config.KeyStroke) { + aerc.pendingKeys = []config.KeyStroke{} + for _, stroke := range strokes { + simulated := tcell.NewEventKey( + stroke.Key, stroke.Rune, tcell.ModNone) + aerc.Event(simulated) + } +} + func (aerc *Aerc) Event(event tcell.Event) bool { if aerc.focused != nil { return aerc.focused.Event(event) @@ -105,18 +123,30 @@ func (aerc *Aerc) Event(event tcell.Event) bool { Key: event.Key(), Rune: event.Rune(), }) - result, output := aerc.conf.Lbinds.GetBinding(aerc.pendingKeys) + bindings := aerc.getBindings() + incomplete := false + result, strokes := bindings.GetBinding(aerc.pendingKeys) switch result { case config.BINDING_FOUND: - aerc.pendingKeys = []config.KeyStroke{} - for _, stroke := range output { - simulated := tcell.NewEventKey( - stroke.Key, stroke.Rune, tcell.ModNone) - aerc.Event(simulated) - } + aerc.simulate(strokes) + return true case config.BINDING_INCOMPLETE: - return false + incomplete = true case config.BINDING_NOT_FOUND: + } + if bindings.Globals { + result, strokes = aerc.conf.Bindings.Global. + GetBinding(aerc.pendingKeys) + switch result { + case config.BINDING_FOUND: + aerc.simulate(strokes) + return true + case config.BINDING_INCOMPLETE: + incomplete = true + case config.BINDING_NOT_FOUND: + } + } + if !incomplete { aerc.pendingKeys = []config.KeyStroke{} if event.Rune() == ':' { aerc.BeginExCommand()