Jonathan Bartlett: 1 Account Specific Bindings 3 files changed, 165 insertions(+), 54 deletions(-)
Hi Reto,
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/aerc/patches/22659/mbox | git am -3Learn more about email & git
--- config/config.go | 150 +++++++++++++++++++++++++++++------------- doc/aerc-config.5.scd | 15 +++++ widgets/aerc.go | 54 ++++++++++++--- 3 files changed, 165 insertions(+), 54 deletions(-) diff --git a/config/config.go b/config/config.go index af9c63b..528c134 100644 --- a/config/config.go +++ b/config/config.go @@ -90,14 +90,19 @@ type AccountConfig struct { } type BindingConfig struct { - Global *KeyBindings - AccountWizard *KeyBindings - Compose *KeyBindings - ComposeEditor *KeyBindings - ComposeReview *KeyBindings - MessageList *KeyBindings - MessageView *KeyBindings - Terminal *KeyBindings + Global *BindingGroup + AccountWizard *BindingGroup + Compose *BindingGroup + ComposeEditor *BindingGroup + ComposeReview *BindingGroup + MessageList *BindingGroup + MessageView *BindingGroup + Terminal *BindingGroup +} + +type BindingGroup struct { + Base *KeyBindings + Account map[string]*KeyBindings } type ComposeConfig struct { @@ -478,16 +483,10 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { } file.NameMapper = mapName config := &AercConfig{ - Bindings: BindingConfig{ - Global: NewKeyBindings(), - AccountWizard: NewKeyBindings(), - Compose: NewKeyBindings(), - ComposeEditor: NewKeyBindings(), - ComposeReview: NewKeyBindings(), - MessageList: NewKeyBindings(), - MessageView: NewKeyBindings(), - Terminal: NewKeyBindings(), - }, + Bindings: BindingConfig{ + AccountWizard: &BindingGroup{Base: NewKeyBindings()}, + }, + Ini: file, Ui: UIConfig{ @@ -542,12 +541,12 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { }, } - // These bindings are not configurable - config.Bindings.AccountWizard.ExKey = KeyStroke{ + // These bindings are not configurable + config.Bindings.AccountWizard.Base.ExKey = KeyStroke{ Key: tcell.KeyCtrlE, } quit, _ := ParseBinding("<C-q>", ":quit<Enter>") - config.Bindings.AccountWizard.Add(quit) + config.Bindings.AccountWizard.Base.Add(quit) if err = config.LoadConfig(file); err != nil { return nil, err @@ -565,6 +564,7 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { } else { config.Accounts = accounts } + filename = path.Join(*root, "binds.conf") binds, err := ini.Load(filename) if err != nil { @@ -575,25 +575,86 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { 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, - "compose::editor": &config.Bindings.ComposeEditor, - "compose::review": &config.Bindings.ComposeReview, - } + bindSectionMap := map[string][]string { + "default": []string{}, + "compose": []string{}, + "messages": []string{}, + "terminal": []string{}, + "view": []string{}, + "compose::editor": []string{}, + "compose::review": []string{}, + } for _, name := range binds.SectionStrings() { - sec, err := binds.GetSection(name) + parts := strings.Split(name, "//") + base := parts[0] + section, ok := bindSectionMap[strings.ToLower(base)] + if !ok { + return nil, errors.New("Invalid section name " + name + ", Base was: " + base) + } + section = append(section, name) + bindSectionMap[strings.ToLower(base)] = section + } + + acctNames := []string{} + for _, acctConf := range config.Accounts { + acctNames = append(acctNames, acctConf.Name) + } + + bindingGroups := make(map[string]*BindingGroup) + for baseName, secNames := range bindSectionMap { + group, err := NewBindingGroup(binds, acctNames, secNames, baseName) + if err != nil { + return nil, err + } + bindingGroups[baseName] = group + } + + config.Bindings.Global = bindingGroups["default"] + config.Bindings.Compose = bindingGroups["compose"] + config.Bindings.ComposeEditor = bindingGroups["compose::editor"] + config.Bindings.ComposeReview = bindingGroups["compose::review"] + config.Bindings.MessageList = bindingGroups["messages"] + config.Bindings.MessageView = bindingGroups["view"] + config.Bindings.Terminal = bindingGroups["terminal"] + + fmt.Printf("%:v", config.Bindings.MessageList.Account)
Second, could we structure this a bit like the styles? It's the same problem and I think a bit cleaner than what you are doing. In my opinion it would be nice if we don't invent yet another notation, meaning reuse the same contextual logic we already have in the aerc-config(5) contextual UI config section. In other words, separate context with ":" (yes, the :: section separator is a bit annoying, but we should be able to manage easily), followed by account=$something.Jonathan Bartlett <jsb@jonnobrow.co.uk>I will switch to this too, I completely agree that it makes more sense. Thanks for your feedback, I will make those changes and submit an updated patch in the coming days. Jonathan.That way we can add more contexts in the future and it is symmetrical. What do you think? Cheers, Reto
+ + // Globals can't inherit from themselves + config.Bindings.Global.Base.Globals = false + for _, acctBinds := range config.Bindings.Global.Account { + acctBinds.Globals = false + } + return config, nil +} + +func NewBindingGroup(binds *ini.File, accountNames []string, secNames []string, baseName string) (*BindingGroup, error) { + group := &BindingGroup{ + Base: NewKeyBindings(), Account: make(map[string]*KeyBindings) } + for _, name := range accountNames { + group.Account[strings.ToLower(name)] = NewKeyBindings() + } + for _, name := range secNames { + parts := strings.Split(name, "//") + + 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) - } + + subgroup := &KeyBindings{} + if (len(parts) == 2) { + sub, ok := group.Account[strings.ToLower(parts[1])] + if !ok { + return nil, errors.New("Invalid binding subgroup name " + name) + } + subgroup = sub + } else if (len(parts) == 1) { + subgroup = group.Base + } else { + return nil, errors.New("Invalid binding group name " + name) + } + bindings := NewKeyBindings() for key, value := range sec.KeysHash() { if key == "$ex" { @@ -602,8 +663,7 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { return nil, err } if len(strokes) != 1 { - return nil, errors.New( - "Error: only one keystroke supported for $ex") + return nil, errors.New("Invalid binding") } bindings.ExKey = strokes[0] continue @@ -613,8 +673,7 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { continue } if value != "true" { - return nil, errors.New( - "Error: expected 'true' or 'false' for $noinherit") + return nil, errors.New("Invalid binding") } bindings.Globals = false continue @@ -625,11 +684,14 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) { } bindings.Add(binding) } - *group = MergeBindings(bindings, *group) - } - // Globals can't inherit from themselves - config.Bindings.Global.Globals = false - return config, nil + + if len(parts) == 1 { + group.Base = MergeBindings(subgroup, bindings) + } else { + group.Account[strings.ToLower(parts[1])] = MergeBindings(subgroup, bindings) + } + } + return group, nil } // checkConfigPerms checks for too open permissions diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd index d4de883..511864c 100644 --- a/doc/aerc-config.5.scd +++ b/doc/aerc-config.5.scd @@ -487,6 +487,21 @@ are: *[terminal]* keybindings for terminal tabs +You may also configure account specific key bindings for each context: + +*[context//accountname]* + keybindings for this context and account, where *accountname* matches + the name provided in *accounts.conf* + +Example: +``` +[messages//mailbox] +c = :cf path:mailbox/** and<space> + +[compose::editor//mailbox2] +... +``` + You may also configure global keybindings by placing them at the beginning of the file, before specifying any context-specific sections. For each *key=value* option specified, the _key_ is the keystrokes pressed (in order) to invoke this diff --git a/widgets/aerc.go b/widgets/aerc.go index 6df0c95..d515536 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -182,26 +182,60 @@ func (aerc *Aerc) Draw(ctx *ui.Context) { } func (aerc *Aerc) getBindings() *config.KeyBindings { + selectedAccountName := "" + if aerc.SelectedAccount() != nil { + selectedAccountName = strings.ToLower(aerc.SelectedAccount().acct.Name); + } switch view := aerc.SelectedTab().(type) { case *AccountView: - return aerc.conf.Bindings.MessageList + acctBinds, ok := aerc.conf.Bindings.MessageList.Account[selectedAccountName] + if !ok { + return aerc.conf.Bindings.MessageList.Base + } else { + return config.MergeBindings(aerc.conf.Bindings.MessageList.Base, acctBinds) + } case *AccountWizard: - return aerc.conf.Bindings.AccountWizard + return aerc.conf.Bindings.AccountWizard.Base case *Composer: switch view.Bindings() { case "compose::editor": - return aerc.conf.Bindings.ComposeEditor + acctBinds, ok := aerc.conf.Bindings.ComposeEditor.Account[selectedAccountName] + if !ok { + return aerc.conf.Bindings.ComposeEditor.Base + } else { + return config.MergeBindings(aerc.conf.Bindings.ComposeEditor.Base, acctBinds) + } case "compose::review": - return aerc.conf.Bindings.ComposeReview + acctBinds, ok := aerc.conf.Bindings.ComposeReview.Account[selectedAccountName] + if !ok { + return aerc.conf.Bindings.ComposeReview.Base + } else { + return config.MergeBindings(aerc.conf.Bindings.ComposeReview.Base, acctBinds) + } default: - return aerc.conf.Bindings.Compose + acctBinds, ok := aerc.conf.Bindings.Compose.Account[selectedAccountName] + if !ok { + return aerc.conf.Bindings.Compose.Base + } else { + return config.MergeBindings(aerc.conf.Bindings.Compose.Base, acctBinds) + } } case *MessageViewer: - return aerc.conf.Bindings.MessageView + acctBinds, ok := aerc.conf.Bindings.MessageView.Account[selectedAccountName] + if !ok { + return aerc.conf.Bindings.MessageView.Base + } else { + return config.MergeBindings(aerc.conf.Bindings.MessageView.Base, acctBinds) + } case *Terminal: - return aerc.conf.Bindings.Terminal + return aerc.conf.Bindings.Terminal.Base default: - return aerc.conf.Bindings.Global + acctBinds, ok := aerc.conf.Bindings.Global.Account[selectedAccountName] + if !ok { + return aerc.conf.Bindings.Global.Base + } else { + return config.MergeBindings(aerc.conf.Bindings.Global.Base, acctBinds) + } } } @@ -245,7 +279,7 @@ func (aerc *Aerc) Event(event tcell.Event) bool { case config.BINDING_NOT_FOUND: } if bindings.Globals { - result, strokes = aerc.conf.Bindings.Global. + result, strokes = aerc.conf.Bindings.Global.Base. GetBinding(aerc.pendingKeys) switch result { case config.BINDING_FOUND: @@ -261,7 +295,7 @@ func (aerc *Aerc) Event(event tcell.Event) bool { exKey := bindings.ExKey if aerc.simulating > 0 { // Keybindings still use : even if you change the ex key - exKey = aerc.conf.Bindings.Global.ExKey + exKey = aerc.conf.Bindings.Global.Base.ExKey } if event.Key() == exKey.Key && event.Rune() == exKey.Rune { aerc.BeginExCommand("") -- 2.31.1
Hi, Thank you for the patch. I like the functionality this wants to implement, however I think this patch could be improved a bit. For starters, it has severe whitespace issues, please run go fmt on anything you send to the list. Also, do remove debug statements please ;)