~sircmpwn/aerc

Account Specific Bindings v3 PROPOSED

Jonathan Bartlett: 1
 Account Specific Bindings

 3 files changed, 165 insertions(+), 54 deletions(-)
Hi Reto,
Next
Export patchset (mbox)
How do I use this?

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 -3
Learn more about email & git

[PATCH v3] Account Specific Bindings Export this patch

---
 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)

    // 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 ;)