aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoni Marti <koni.marti@gmail.com>2023-05-10 23:56:25 +0200
committerRobin Jarry <robin@jarry.cc>2023-05-16 13:41:02 +0200
commit42cd415756bff13acd306db21eff5f4439665270 (patch)
tree9fe9bfe9d3224f88961f922fcf5a6b6ba23c8bab
parent20747fcf104aa05bdc4d7d2b12a2488940bcae2f (diff)
downloadaerc-42cd415756bff13acd306db21eff5f4439665270.tar.gz
aerc-42cd415756bff13acd306db21eff5f4439665270.zip
commands: execute commands with templates
Execute template code before running a command. With this, we can use templates in our keybinds like: [messages] E = :'{{if match (.Flags|join "") "O"}}envelope{{else}}view{{end}}'<Enter> Signed-off-by: Koni Marti <koni.marti@gmail.com> Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r--commands/commands.go70
-rw-r--r--commands/commands_test.go54
2 files changed, 106 insertions, 18 deletions
diff --git a/commands/commands.go b/commands/commands.go
index c6f95aaa..7dfd183b 100644
--- a/commands/commands.go
+++ b/commands/commands.go
@@ -103,42 +103,76 @@ func templateData(
data.SetAccount(cfg)
data.SetFolder(folder)
data.SetInfo(msg, 0, false)
+ if acct != nil {
+ acct.SetStatus(func(s *state.AccountState, _ string) {
+ data.SetState(s)
+ })
+ }
return &data
}
func (cmds *Commands) ExecuteCommand(
aerc *widgets.Aerc,
- args []string,
+ origArgs []string,
account *config.AccountConfig,
msg *models.MessageInfo,
) error {
- if len(args) == 0 {
+ if len(origArgs) == 0 {
return errors.New("Expected a command.")
}
+ data := templateData(aerc, account, msg)
+ args, err := expand(data, origArgs)
+ if err != nil {
+ return err
+ }
if cmd, ok := cmds.dict()[args[0]]; ok {
log.Tracef("executing command %v", args)
- var buf bytes.Buffer
- data := templateData(aerc, account, msg)
+ return cmd.Execute(aerc, args)
+ }
+ return NoSuchCommand(args[0])
+}
- processedArgs := make([]string, len(args))
- for i, arg := range args {
- t, err := templates.ParseTemplate(arg, arg)
- if err != nil {
- return err
+// expand expands template expressions and returns a new slice of arguments
+func expand(data models.TemplateData, origArgs []string) ([]string, error) {
+ args := make([]string, len(origArgs))
+ copy(args, origArgs)
+
+ c := strings.Join(origArgs, "")
+ isTemplate := strings.Contains(c, "{{") || strings.Contains(c, "}}")
+
+ if isTemplate {
+ for i := range args {
+ if strings.Contains(args[i], " ") {
+ q := "\""
+ if strings.ContainsAny(args[i], "\"") {
+ q = "'"
+ }
+ args[i] = q + args[i] + q
}
- err = templates.Render(t, &buf, data)
- if err != nil {
- return err
- }
- arg = buf.String()
- buf.Reset()
- processedArgs[i] = arg
}
- return cmd.Execute(aerc, processedArgs)
+ cmdline := strings.Join(args, " ")
+ log.Tracef("template data found in: %v", cmdline)
+
+ t, err := templates.ParseTemplate("execute", cmdline)
+ if err != nil {
+ return nil, err
+ }
+
+ var buf bytes.Buffer
+ err = templates.Render(t, &buf, data)
+ if err != nil {
+ return nil, err
+ }
+
+ args, err = splitCmd(buf.String())
+ if err != nil {
+ return nil, err
+ }
}
- return NoSuchCommand(args[0])
+
+ return args, nil
}
func GetTemplateCompletion(
diff --git a/commands/commands_test.go b/commands/commands_test.go
new file mode 100644
index 00000000..a424d16d
--- /dev/null
+++ b/commands/commands_test.go
@@ -0,0 +1,54 @@
+package commands
+
+import (
+ "reflect"
+ "testing"
+
+ "git.sr.ht/~rjarry/aerc/lib/state"
+)
+
+func TestExecuteCommand_expand(t *testing.T) {
+ tests := []struct {
+ args []string
+ want []string
+ }{
+ {
+ args: []string{"prompt", "Really quit? ", "quit"},
+ want: []string{"prompt", "Really quit? ", "quit"},
+ },
+ {
+ args: []string{"{{", "print", "\"hello\"", "}}"},
+ want: []string{"hello"},
+ },
+ {
+ args: []string{"prompt", "Really quit ? ", " quit "},
+ want: []string{"prompt", "Really quit ? ", " quit "},
+ },
+ {
+ args: []string{
+ "prompt", "Really quit? ", "{{",
+ "print", "\"quit\"", "}}",
+ },
+ want: []string{"prompt", "Really quit? ", "quit"},
+ },
+ {
+ args: []string{
+ "prompt", "Really quit? ", "{{",
+ "if", "1", "}}", "quit", "{{end}}",
+ },
+ want: []string{"prompt", "Really quit? ", "quit"},
+ },
+ }
+
+ data := state.TemplateData{}
+
+ for i, test := range tests {
+ got, err := expand(&data, test.args)
+ if err != nil {
+ t.Errorf("test %d failed with err: %v", i, err)
+ } else if !reflect.DeepEqual(got, test.want) {
+ t.Errorf("test %d failed: "+
+ "got: %v, but want: %v", i, got, test.want)
+ }
+ }
+}