~sircmpwn/aerc

Add Templates v1 PROPOSED

Nice work. I've added some comments and have a couple of questions:

Should other commands e.g. reply or forward have a template
available as well? If so, then it could be a good idea to put some of
this logic in a lib file.
Also, could you add docs for the command in doc/aerc.1.scd?
Okay, so you mentioned this in the ticket and went with the easier
approach - but hear me out. If we put this into the composor and make it
work for replies, we can generalize this nicely by using golang text
templates. Then, the default reply template becomes something like this:

On {{.Date}}, {{.From.Name}} wrote:
{{quote(.Original)}}

{{cursor()}}

I don't recall exactly how text templates work, but something like this.
The quote function would quote and wrap the original email at 72
columns, and the cursor() function would emit an empty string but would
magically record the current index and move the cursor there.

Thoughts?
> Okay, so you mentioned this in the ticket and went with the easier
> approach - but hear me out. If we put this into the composor and make it
> work for replies, we can generalize this nicely by using golang text
> templates. Then, the default reply template becomes something like this:
> 
> On {{.Date}}, {{.From.Name}} wrote:
> {{quote(.Original)}}
> 
> {{cursor()}}
> 
> I don't recall exactly how text templates work, but something like this.
> The quote function would quote and wrap the original email at 72
> columns, and the cursor() function would emit an empty string but would
> magically record the current index and move the cursor there.
> 
> Thoughts?
I agree that going with this approach makes templates more useful and
clears up a lot of things about how we can make templates work for 
replies and forwards.

I am happy to continue working on a implementation inside the
composer using golang text templates. But I am still getting my head
around golang and how aerc works.

I will read up and ask more questions on some parts if that is fine.
--
Cheers,
Srivathsan Murali (sri)
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/%3C20191008091901.93150-1-sri%40vathsan.com%3E/mbox | git am -3
Learn more about email & git

[PATCH] Add Templates Export this patch

This adds the ability to load templates into the body of the
email. When compose is called with '-T' flag, the default template
or the template with the name passed as value is used to populate
the body of the email.

There are two new configs options for accounts.conf: TemplateDir
and DefaultTemplate. Template Dir specifies the directory containing
the templates.DefaultTemplate specifies the template that is loaded
if no template name is provided.
---
Implementation for #60: Email Templates
 commands/account/compose.go | 43 ++++++++++++++++++++++++++++++++-----
 config/config.go            |  2 ++
 doc/aerc-config.5.scd       |  7 ++++++
 3 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/commands/account/compose.go b/commands/account/compose.go
index 039eb92363c6..d8d97970bad8 100644
--- a/commands/account/compose.go
+++ b/commands/account/compose.go
@@ -2,11 +2,15 @@ package account
 
 import (
 	"errors"
+	"io/ioutil"
+	"path"
 	"regexp"
 	"strings"
 
 	"git.sr.ht/~sircmpwn/aerc/widgets"
 	"git.sr.ht/~sircmpwn/getopt"
+	"git.sr.ht/~sircmpwn/aerc/config"
+	"github.com/mitchellh/go-homedir"
 )
 
 type Compose struct{}
@@ -24,11 +28,11 @@ func (Compose) Complete(aerc *widgets.Aerc, args []string) []string {
 }
 
 func (Compose) Execute(aerc *widgets.Aerc, args []string) error {
-	body, err := buildBody(args)
+	acct := aerc.SelectedAccount()
+	body, err := buildBody(args, acct.AccountConfig())
 	if err != nil {
 		return err
 	}
-	acct := aerc.SelectedAccount()
 	composer := widgets.NewComposer(aerc,
 		aerc.Config(), acct.AccountConfig(), acct.Worker(), nil)
 	tab := aerc.NewTab(composer, "New email")
@@ -44,9 +48,9 @@ func (Compose) Execute(aerc *widgets.Aerc, args []string) error {
 	return nil
 }
 
-func buildBody(args []string) (string, error) {
+func buildBody(args []string, acct *config.AccountConfig) (string, error) {
 	var body, headers string
-	opts, optind, err := getopt.Getopts(args, "H:")
+	opts, optind, err := getopt.Getopts(args, ":T:H:")
 	if err != nil {
 		return "", err
 	}
@@ -60,11 +64,25 @@ func buildBody(args []string) (string, error) {
 			} else {
 				headers += opt.Value + ":\n"
 			}
+		case 'T':
+			template, err := readTemplateFromFile(opt.Value, acct.TemplateDir)
+			if err != nil {
+				return "", err
+			}
+			body = template
+		case ':':
+			if opt.Value == "T" {
+				template, err := readTemplateFromFile(acct.DefaultTemplate, acct.TemplateDir)
+				if err != nil {
+					return "", err
+				}
+				body = template
+			}
 		}
 	}
 	posargs := args[optind:]
 	if len(posargs) > 1 {
-		return "", errors.New("Usage: compose [-H] [body]")
+		return "", errors.New("Usage: compose [-H] [-T template] [body]")
 	}
 	if len(posargs) == 1 {
 		body = posargs[0]
@@ -78,3 +96,18 @@ func buildBody(args []string) (string, error) {
 	}
 	return body, nil
 }
+
+func readTemplateFromFile(templateName string, templateDir string)  (string, error) {
+	if templateDir == "" {
+		return "", errors.New("Template Directory is not set.")
+	}
+	templateFile, err := homedir.Expand(path.Join(templateDir, templateName))
+	if err != nil {
+		return "", errors.New("Error while expanding template path.")
+	}
+	template, err := ioutil.ReadFile(templateFile)
+	if err != nil {
+		return "", errors.New("Error while reading template.")
+	}
+	return string(template), nil
+}
diff --git a/config/config.go b/config/config.go
index 133a7f4ed5ca..a7ca2f27aed2 100644
--- a/config/config.go
+++ b/config/config.go
@@ -59,6 +59,8 @@ type AccountConfig struct {
 	OutgoingCredCmd string
 	SignatureFile   string
 	SignatureCmd    string
+	TemplateDir     string
+	DefaultTemplate string
 }
 
 type BindingConfig struct {
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index f4f02f2cb0f3..2be0c425a5c1 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -320,6 +320,13 @@ Note that many of these configuration options are written for you, such as
 	signature to be added to emails sent from this account. If the command
 	fails then *signature-file* is used instead.
 
+*template-dir*
+	Specifies the location of the folder containing email templates.
+
+*default-template*
+	Specifies the name of the default template used when no template
+	is given as part of the compose command.
+
 # BINDS.CONF
 
 This file is used for configuring keybindings used in the aerc interactive
-- 
2.23.0
Nice work. I've added some comments and have a couple of questions:

Should other commands e.g. reply or forward have a template
available as well? If so, then it could be a good idea to put some of
this logic in a lib file.

Also, could you add docs for the command in doc/aerc.1.scd?
Okay, so you mentioned this in the ticket and went with the easier
approach - but hear me out. If we put this into the composor and make it
work for replies, we can generalize this nicely by using golang text
templates. Then, the default reply template becomes something like this:

On {{.Date}}, {{.From.Name}} wrote:
{{quote(.Original)}}

{{cursor()}}

I don't recall exactly how text templates work, but something like this.
The quote function would quote and wrap the original email at 72
columns, and the cursor() function would emit an empty string but would
magically record the current index and move the cursor there.

Thoughts?
View this thread in the archives