This thread contains a patchset. You're looking at the original emails,
but you may wish to use the patch review UI.
Review patch
5
3
[PATCH v4 1/3] Account Specific Bindings
---
config/config.go | 148 +++++++++++++++++++++++++++++ -------------
doc/aerc-config.5.scd | 15 +++++
widgets/aerc.go | 54 ++++++++++++ ---
3 files changed, 163 insertions(+), 54 deletions(-)
diff --git a/config/config.go b/config/config.go
index af9c63b..3ecf2b7 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,84 @@ 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"]
+
+ // 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 +661,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 +671,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 +682,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(bindings, subgroup)
+ } else {
+ group.Account[strings.ToLower(parts[1])] = MergeBindings(bindings, subgroup)
+ }
+ }
+ 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
[PATCH v4 2/3] Formatting
---
config/config.go | 176 +++++++++++++++++++++++ ------------------------
widgets/aerc.go | 82 +++++++++++ -----------
2 files changed, 129 insertions(+), 129 deletions(-)
diff --git a/config/config.go b/config/config.go
index 3ecf2b7..4f39f29 100644
--- a/config/config.go
+++ b/config/config.go
@@ -90,19 +90,19 @@ type AccountConfig struct {
}
type BindingConfig struct {
- Global *BindingGroup
- AccountWizard *BindingGroup
- Compose *BindingGroup
- ComposeEditor *BindingGroup
- ComposeReview *BindingGroup
- MessageList *BindingGroup
- MessageView *BindingGroup
- Terminal *BindingGroup
+ 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
+ Base *KeyBindings
+ Account map[string]*KeyBindings
}
type ComposeConfig struct {
@@ -483,9 +483,9 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
}
file.NameMapper = mapName
config := &AercConfig{
- Bindings: BindingConfig{
- AccountWizard: &BindingGroup{Base: NewKeyBindings()},
- },
+ Bindings: BindingConfig{
+ AccountWizard: &BindingGroup{Base: NewKeyBindings()},
+ },
Ini: file,
@@ -541,7 +541,7 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
},
}
- // These bindings are not configurable
+ // These bindings are not configurable
config.Bindings.AccountWizard.Base.ExKey = KeyStroke{
Key: tcell.KeyCtrlE,
}
@@ -576,82 +576,82 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
}
}
- bindSectionMap := map[string][]string {
- "default": []string{},
- "compose": []string{},
- "messages": []string{},
- "terminal": []string{},
- "view": []string{},
- "compose::editor": []string{},
- "compose::review": []string{},
- }
+ 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() {
- 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"]
-
- // Globals can't inherit from themselves
+ 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"]
+
+ // Globals can't inherit from themselves
config.Bindings.Global.Base.Globals = false
- for _, acctBinds := range config.Bindings.Global.Account {
- acctBinds.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)
+ 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
}
- 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)
- }
+ 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() {
@@ -682,14 +682,14 @@ func NewBindingGroup(binds *ini.File, accountNames []string, secNames []string,
}
bindings.Add(binding)
}
-
- if len(parts) == 1 {
- group.Base = MergeBindings(bindings, subgroup)
- } else {
- group.Account[strings.ToLower(parts[1])] = MergeBindings(bindings, subgroup)
- }
- }
- return group, nil
+
+ if len(parts) == 1 {
+ group.Base = MergeBindings(bindings, subgroup)
+ } else {
+ group.Account[strings.ToLower(parts[1])] = MergeBindings(bindings, subgroup)
+ }
+ }
+ return group, nil
}
// checkConfigPerms checks for too open permissions
diff --git a/widgets/aerc.go b/widgets/aerc.go
index d515536..35efa3b 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -182,60 +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);
- }
+ selectedAccountName := ""
+ if aerc.SelectedAccount() != nil {
+ selectedAccountName = strings.ToLower(aerc.SelectedAccount().acct.Name)
+ }
switch view := aerc.SelectedTab().(type) {
case *AccountView:
- 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)
- }
+ 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.Base
case *Composer:
switch view.Bindings() {
case "compose::editor":
- 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)
- }
+ 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":
- 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)
- }
+ 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:
- 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)
- }
+ 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:
- 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)
- }
+ 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.Base
+ return aerc.conf.Bindings.Terminal.Base
default:
- 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)
- }
+ 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)
+ }
}
}
--
2.31.1
[PATCH v4 3/3] Reworked account specific bindings
Account specific bindings now follows the same structure as contextual
UI configuration. It also supports regular expressions and provides
a base for further binding subcontexts in the future.
---
config/bindings.go | 22 ++++
config/config.go | 240 ++++++++++++++++++++++ --------------------
doc/aerc-config.5.scd | 10 + -
widgets/aerc.go | 52 ++ -------
4 files changed, 166 insertions(+), 158 deletions(-)
diff --git a/config/bindings.go b/config/bindings.go
index 9956b41..23a082e 100644
--- a/config/bindings.go
+++ b/config/bindings.go
@@ -54,6 +54,28 @@ func MergeBindings(bindings ...*KeyBindings) *KeyBindings {
return merged
}
+ func (config AercConfig) MergeContextualBinds(baseBinds *KeyBindings,
+ contextType ContextType, reg string, bindCtx string) *KeyBindings {
+
+ bindings := baseBinds
+ for _, contextualBind := range config.ContextualBinds {
+ if contextualBind.ContextType != contextType {
+ continue
+ }
+
+ if !contextualBind.Regex.Match([]byte(reg)) {
+ continue
+ }
+
+ if contextualBind.BindContext != bindCtx {
+ continue
+ }
+
+ bindings = MergeBindings(bindings, contextualBind.Bindings)
+ }
+ return bindings
+ }
+
func (bindings *KeyBindings) Add(binding *Binding) {
// TODO: Search for conflicts?
bindings.bindings = append(bindings.bindings, binding)
diff --git a/config/config.go b/config/config.go
index 4f39f29..cfc1757 100644
--- a/config/config.go
+++ b/config/config.go
@@ -56,6 +56,7 @@ const (
UI_CONTEXT_FOLDER ContextType = iota
UI_CONTEXT_ACCOUNT
UI_CONTEXT_SUBJECT
+ BIND_CONTEXT_ACCOUNT
)
type UIConfigContext struct {
@@ -90,19 +91,21 @@ type AccountConfig struct {
}
type BindingConfig struct {
- Global *BindingGroup
- AccountWizard *BindingGroup
- Compose *BindingGroup
- ComposeEditor *BindingGroup
- ComposeReview *BindingGroup
- MessageList *BindingGroup
- MessageView *BindingGroup
- Terminal *BindingGroup
+ Global *KeyBindings
+ AccountWizard *KeyBindings
+ Compose *KeyBindings
+ ComposeEditor *KeyBindings
+ ComposeReview *KeyBindings
+ MessageList *KeyBindings
+ MessageView *KeyBindings
+ Terminal *KeyBindings
}
- type BindingGroup struct {
- Base *KeyBindings
- Account map[string]*KeyBindings
+ type BindingConfigContext struct {
+ ContextType ContextType
+ Regex *regexp.Regexp
+ Bindings *KeyBindings
+ BindContext string
}
type ComposeConfig struct {
@@ -139,17 +142,18 @@ type TemplateConfig struct {
}
type AercConfig struct {
- Bindings BindingConfig
- Compose ComposeConfig
- Ini *ini.File `ini:"-"`
- Accounts []AccountConfig `ini:"-"`
- Filters []FilterConfig `ini:"-"`
- Viewer ViewerConfig `ini:"-"`
- Triggers TriggersConfig `ini:"-"`
- Ui UIConfig
- ContextualUis []UIConfigContext
- General GeneralConfig
- Templates TemplateConfig
+ Bindings BindingConfig
+ ContextualBinds []BindingConfigContext
+ Compose ComposeConfig
+ Ini *ini.File `ini:"-"`
+ Accounts []AccountConfig `ini:"-"`
+ Filters []FilterConfig `ini:"-"`
+ Viewer ViewerConfig `ini:"-"`
+ Triggers TriggersConfig `ini:"-"`
+ Ui UIConfig
+ ContextualUis []UIConfigContext
+ General GeneralConfig
+ Templates TemplateConfig
}
// Input: TimestampFormat
@@ -350,11 +354,13 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
}
}
}
+
if ui, err := file.GetSection("ui"); err == nil {
if err := ui.MapTo(&config.Ui); err != nil {
return err
}
}
+
for _, sectionName := range file.SectionStrings() {
if !strings.Contains(sectionName, "ui:") {
continue
@@ -484,9 +490,18 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
file.NameMapper = mapName
config := &AercConfig{
Bindings: BindingConfig{
- AccountWizard: &BindingGroup{Base: NewKeyBindings()},
+ Global: NewKeyBindings(),
+ AccountWizard: NewKeyBindings(),
+ Compose: NewKeyBindings(),
+ ComposeEditor: NewKeyBindings(),
+ ComposeReview: NewKeyBindings(),
+ MessageList: NewKeyBindings(),
+ MessageView: NewKeyBindings(),
+ Terminal: NewKeyBindings(),
},
+ ContextualBinds: []BindingConfigContext{},
+
Ini: file,
Ui: UIConfig{
@@ -542,11 +557,11 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
}
// These bindings are not configurable
- config.Bindings.AccountWizard.Base.ExKey = KeyStroke{
+ config.Bindings.AccountWizard.ExKey = KeyStroke{
Key: tcell.KeyCtrlE,
}
quit, _ := ParseBinding("<C-q>", ":quit<Enter>")
- config.Bindings.AccountWizard.Base.Add(quit)
+ config.Bindings.AccountWizard.Add(quit)
if err = config.LoadConfig(file); err != nil {
return nil, err
@@ -576,120 +591,121 @@ func LoadConfigFromFile(root *string, sharedir string) (*AercConfig, error) {
}
}
- bindSectionMap := map[string][]string{
- "default": []string{},
- "compose": []string{},
- "messages": []string{},
- "terminal": []string{},
- "view": []string{},
- "compose::editor": []string{},
- "compose::review": []string{},
+ baseGroups := 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,
}
- for _, name := range binds.SectionStrings() {
- 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)
+
+ // Base Bindings
+ for name, group := range baseGroups {
+ err = config.LoadBinds(binds, name, group)
+ if err != nil {
+ return nil, err
}
- section = append(section, name)
- bindSectionMap[strings.ToLower(base)] = section
}
- acctNames := []string{}
- for _, acctConf := range config.Accounts {
- acctNames = append(acctNames, acctConf.Name)
+ config.Bindings.Global.Globals = false
+ for _, contextBind := range config.ContextualBinds {
+ if contextBind.BindContext == "default" {
+ contextBind.Bindings.Globals = false
+ }
}
- bindingGroups := make(map[string]*BindingGroup)
- for baseName, secNames := range bindSectionMap {
- group, err := NewBindingGroup(binds, acctNames, secNames, baseName)
+ return config, nil
+ }
+
+ func LoadBindingSection(sec *ini.Section) (*KeyBindings, error) {
+ 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("Invalid binding")
+ }
+ bindings.ExKey = strokes[0]
+ continue
+ }
+ if key == "$noinherit" {
+ if value == "false" {
+ continue
+ }
+ if value != "true" {
+ return nil, errors.New("Invalid binding")
+ }
+ bindings.Globals = false
+ continue
+ }
+ binding, err := ParseBinding(key, value)
if err != nil {
return nil, err
}
- bindingGroups[baseName] = group
+ bindings.Add(binding)
}
+ return bindings, nil
+ }
- 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"]
+ func (config *AercConfig) LoadBinds(binds *ini.File, baseName string, baseGroup **KeyBindings) error {
- // Globals can't inherit from themselves
- config.Bindings.Global.Base.Globals = false
- for _, acctBinds := range config.Bindings.Global.Account {
- acctBinds.Globals = false
+ if sec, err := binds.GetSection(baseName); err == nil {
+ binds, err := LoadBindingSection(sec)
+ if err != nil {
+ return err
+ }
+ *baseGroup = MergeBindings(binds, *baseGroup)
}
- 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, "//")
+ for _, sectionName := range binds.SectionStrings() {
+ if !strings.Contains(sectionName, baseName+":") ||
+ strings.Contains(sectionName, baseName+"::") {
+ continue
+ }
- sec, err := binds.GetSection(name)
+ bindSection, err := binds.GetSection(sectionName)
if err != nil {
- return nil, err
+ return err
}
- 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)
+ binds, err := LoadBindingSection(bindSection)
+ if err != nil {
+ return err
}
- 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("Invalid binding")
- }
- bindings.ExKey = strokes[0]
- continue
+ contextualBind :=
+ BindingConfigContext{
+ Bindings: binds,
+ BindContext: baseName,
}
- if key == "$noinherit" {
- if value == "false" {
- continue
- }
- if value != "true" {
- return nil, errors.New("Invalid binding")
- }
- bindings.Globals = false
- continue
- }
- binding, err := ParseBinding(key, value)
+
+ var index int
+ if strings.Contains(sectionName, "=") {
+ index = strings.Index(sectionName, "=")
+ value := string(sectionName[index+1:])
+ contextualBind.Regex, err = regexp.Compile(regexp.QuoteMeta(value))
if err != nil {
- return nil, err
+ return err
}
- bindings.Add(binding)
+ } else {
+ return fmt.Errorf("Invalid Bind Context regex in %s", sectionName)
}
- if len(parts) == 1 {
- group.Base = MergeBindings(bindings, subgroup)
- } else {
- group.Account[strings.ToLower(parts[1])] = MergeBindings(bindings, subgroup)
+ switch sectionName[len(baseName)+1 : index] {
+ case "account":
+ contextualBind.ContextType = BIND_CONTEXT_ACCOUNT
+ default:
+ return fmt.Errorf("Unknown Context Bind Section: %s", sectionName)
}
+ config.ContextualBinds = append(config.ContextualBinds, contextualBind)
}
- return group, nil
+
+ return nil
}
// checkConfigPerms checks for too open permissions
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 511864c..fb032cb 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -489,16 +489,16 @@ are:
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*
+ *[context:account=regex]*
+ keybindings for this context and account, where *regex* matches
+ the account name provided in *accounts.conf*
Example:
```
- [messages//mailbox]
+ [messages:account=Mailbox]
c = :cf path:mailbox/** and<space>
- [compose::editor//mailbox2]
+ [compose::editor:account=Mailbox2]
...
```
diff --git a/widgets/aerc.go b/widgets/aerc.go
index 35efa3b..5661260 100644
--- a/widgets/aerc.go
+++ b/widgets/aerc.go
@@ -184,58 +184,28 @@ 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)
+ selectedAccountName = aerc.SelectedAccount().acct.Name
}
switch view := aerc.SelectedTab().(type) {
case *AccountView:
- 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)
- }
+ return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.MessageList, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "messages")
case *AccountWizard:
- return aerc.conf.Bindings.AccountWizard.Base
+ return aerc.conf.Bindings.AccountWizard
case *Composer:
switch view.Bindings() {
case "compose::editor":
- 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)
- }
+ return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.ComposeEditor, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "compose::editor")
case "compose::review":
- 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)
- }
+ return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.ComposeReview, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "compose::review")
default:
- 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)
- }
+ return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.Compose, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "compose")
}
case *MessageViewer:
- 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)
- }
+ return aerc.conf.MergeContextualBinds(aerc.conf.Bindings.MessageView, config.BIND_CONTEXT_ACCOUNT, selectedAccountName, "view")
case *Terminal:
- return aerc.conf.Bindings.Terminal.Base
+ return aerc.conf.Bindings.Terminal
default:
- 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)
- }
+ return aerc.conf.Bindings.Global
}
}
@@ -279,7 +249,7 @@ func (aerc *Aerc) Event(event tcell.Event) bool {
case config.BINDING_NOT_FOUND:
}
if bindings.Globals {
- result, strokes = aerc.conf.Bindings.Global.Base.
+ result, strokes = aerc.conf.Bindings.Global.
GetBinding(aerc.pendingKeys)
switch result {
case config.BINDING_FOUND:
@@ -295,7 +265,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.Base.ExKey
+ exKey = aerc.conf.Bindings.Global.ExKey
}
if event.Key() == exKey.Key && event.Rune() == exKey.Rune {
aerc.BeginExCommand("")
--
2.31.1
Hi,
You seem to have a bit of a problem as to how the email patch submission works.
Maybe follow that tutorial here: https://git-send-email.io/ ?
Please rebase your changes, as explained in that tutorial.
Also, not sure why you tend to send multiple patches in very short time increments...
Maybe slow down a tad on the submission?
Cheers,
Reto
Hi,
> Please rebase your changes, as explained in that tutorial.
I will rebase the changes, should I submit at v4 again or v5 now?
> Also, not sure why you tend to send multiple patches in very short time
> increments...
> Maybe slow down a tad on the submission?
Sorry about the spam, I am still getting used to git send-email.
I will slow down my submissions as I think understand what to do now :)
Thanks,
Jonathan.
On Sun, May 16, 2021 at 05:14:29PM +0100, Jonathan Bartlett wrote:
> I will rebase the changes, should I submit at v4 again or v5 now?
V5 will do