diff options
author | Koni Marti <koni.marti@gmail.com> | 2023-05-10 23:56:24 +0200 |
---|---|---|
committer | Robin Jarry <robin@jarry.cc> | 2023-05-16 13:39:47 +0200 |
commit | 20747fcf104aa05bdc4d7d2b12a2488940bcae2f (patch) | |
tree | 6303754ac9830ff9bd8254b6641f98150e117afc | |
parent | 5c9d22bb84eb8a6ddd4477bae3c081964d6e7b51 (diff) | |
download | aerc-20747fcf104aa05bdc4d7d2b12a2488940bcae2f.tar.gz aerc-20747fcf104aa05bdc4d7d2b12a2488940bcae2f.zip |
commands: expand and complete template code
Expand and complete template code. The exline does understand template
code and can evaluate it. With this patch, the completion systems
supports writing the templates by showing the available template data
fields and template functions. Pressing <Tab> after }} will show the
evaluated template in the completion menu. Complex template code, such
as if-else statements work as well.
Examples:
:filter -f {{<Tab>
will show the possible template data fields and functions
:filter -f {{index (.To|email) 0}}<Tab>
will show the value of the template expression in the completion list
Pressing <Tab> twice after a completed template expression will
substitute the expression with its value.
:{{if match .Folder "INBOX"}}check-mail{{else}}cf INBOX{{end}}
Signed-off-by: Koni Marti <koni.marti@gmail.com>
Acked-by: Robin Jarry <robin@jarry.cc>
-rw-r--r-- | commands/commands.go | 52 | ||||
-rw-r--r-- | lib/templates/template.go | 52 | ||||
-rw-r--r-- | main.go | 3 |
3 files changed, 107 insertions, 0 deletions
diff --git a/commands/commands.go b/commands/commands.go index f50a1e8a..c6f95aaa 100644 --- a/commands/commands.go +++ b/commands/commands.go @@ -141,6 +141,58 @@ func (cmds *Commands) ExecuteCommand( return NoSuchCommand(args[0]) } +func GetTemplateCompletion( + aerc *widgets.Aerc, cmd string, +) ([]string, string, bool) { + args, err := splitCmd(cmd) + if err != nil || len(args) == 0 { + return nil, "", false + } + + countLeft := strings.Count(cmd, "{{") + if countLeft == 0 { + return nil, "", false + } + countRight := strings.Count(cmd, "}}") + + switch { + case countLeft > countRight: + // complete template terms + var i int + for i = len(cmd) - 1; i >= 0; i-- { + if strings.ContainsRune("{()| ", rune(cmd[i])) { + break + } + } + search, prefix := cmd[i+1:], cmd[:i+1] + padding := strings.Repeat(" ", + len(search)-len(strings.TrimLeft(search, " "))) + options := FilterList( + templates.Terms(), + strings.TrimSpace(search), + "", + aerc.SelectedAccountUiConfig().FuzzyComplete, + ) + return options, prefix + padding, true + case countLeft == countRight: + // expand template + data := templateData(aerc, nil, nil) + t, err := templates.ParseTemplate("", cmd) + if err != nil { + log.Warnf("template parsing failed: %v", err) + return nil, "", false + } + var sb strings.Builder + if err = templates.Render(t, &sb, data); err != nil { + log.Warnf("template rendering failed: %v", err) + return nil, "", false + } + return []string{sb.String()}, "", true + } + + return nil, "", false +} + // GetCompletions returns the completion options and the command prefix func (cmds *Commands) GetCompletions( aerc *widgets.Aerc, cmd string, diff --git a/lib/templates/template.go b/lib/templates/template.go index bf257669..6eae591a 100644 --- a/lib/templates/template.go +++ b/lib/templates/template.go @@ -6,6 +6,7 @@ import ( "io" "os" "path" + "reflect" "text/template" "git.sr.ht/~rjarry/aerc/models" @@ -56,3 +57,54 @@ func ParseTemplate(name, content string) (*template.Template, error) { func Render(t *template.Template, w io.Writer, data models.TemplateData) error { return t.Execute(w, data) } + +// builtins is a slice of keywords and functions built into the Go standard +// library for templates. Since they are not exported, they are hardcoded here. +var builtins = []string{ + // from the Go standard library: src/text/template/parse/lex.go + "block", + "break", + "continue", + "define", + "else", + "end", + "if", + "range", + "nil", + "template", + "with", + + // from the Go standard library: src/text/template/funcs.go + "and", + "call", + "html", + "index", + "slice", + "js", + "len", + "not", + "or", + "print", + "printf", + "println", + "urlquery", + "eq", + "ge", + "gt", + "le", + "lt", + "ne", +} + +func Terms() []string { + var s []string + t := reflect.TypeOf((*models.TemplateData)(nil)).Elem() + for i := 0; i < t.NumMethod(); i++ { + s = append(s, "."+t.Method(i).Name) + } + for fnStr := range templateFuncs { + s = append(s, fnStr) + } + s = append(s, builtins...) + return s +} @@ -89,6 +89,9 @@ func execCommand( } func getCompletions(aerc *widgets.Aerc, cmd string) ([]string, string) { + if options, prefix, ok := commands.GetTemplateCompletion(aerc, cmd); ok { + return options, prefix + } var completions []string var prefix string for _, set := range getCommands(aerc.SelectedTabContent()) { |