Leszek Cimała: 6 create OriginalMail struct remove Original* check add .OriginalMIMEType variable to reply template template: remove last \n to fix additional new lines after quote template: add exec and wrap template: man cosmetic changes 17 files changed, 122 insertions(+), 49 deletions(-)
Fixed lines >80 columsn and pushed: To git.sr.ht:~sircmpwn/aerc da6fb1a..a40959c master -> master Thank you! This is a much better approach.
Copy & paste the following snippet into your terminal to import this patchset into git:
curl -s https://lists.sr.ht/~sircmpwn/aerc/patches/9479/mbox | git am -3Learn more about email & git
--- commands/account/compose.go | 3 ++- commands/msg/forward.go | 10 ++++++---- commands/msg/reply.go | 9 +++++---- commands/msg/unsubscribe.go | 2 ++ lib/templates/template.go | 28 ++++++++++++++++------------ models/models.go | 7 +++++++ widgets/aerc.go | 3 ++- widgets/compose.go | 5 +++-- 8 files changed, 43 insertions(+), 24 deletions(-) diff --git a/commands/account/compose.go b/commands/account/compose.go index 24e460b..a8ed679 100644 --- a/commands/account/compose.go +++ b/commands/account/compose.go @@ -5,6 +5,7 @@ import ( "regexp" "strings" + "git.sr.ht/~sircmpwn/aerc/models" "git.sr.ht/~sircmpwn/aerc/widgets" "git.sr.ht/~sircmpwn/getopt" ) @@ -31,7 +32,7 @@ func (Compose) Execute(aerc *widgets.Aerc, args []string) error { acct := aerc.SelectedAccount() composer, err := widgets.NewComposer(aerc, - aerc.Config(), acct.AccountConfig(), acct.Worker(), template, nil) + aerc.Config(), acct.AccountConfig(), acct.Worker(), template, nil, models.OriginalMail{}) if err != nil { return err } diff --git a/commands/msg/forward.go b/commands/msg/forward.go index 35d276e..c51949e 100644 --- a/commands/msg/forward.go +++ b/commands/msg/forward.go @@ -71,14 +71,16 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error { "Subject": subject, } + original := models.OriginalMail{} + addTab := func() (*widgets.Composer, error) { if template != "" { - defaults["OriginalFrom"] = models.FormatAddresses(msg.Envelope.From) - defaults["OriginalDate"] = msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM") + original.From = models.FormatAddresses(msg.Envelope.From) + original.Date = msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM") } composer, err := widgets.NewComposer(aerc, aerc.Config(), acct.AccountConfig(), - acct.Worker(), template, defaults) + acct.Worker(), template, defaults, original) if err != nil { aerc.PushError("Error: " + err.Error()) return nil, err @@ -138,7 +140,7 @@ func (forward) Execute(aerc *widgets.Aerc, args []string) error { store.FetchBodyPart(msg.Uid, msg.BodyStructure, []int{1}, func(reader io.Reader) { buf := new(bytes.Buffer) buf.ReadFrom(reader) - defaults["Original"] = buf.String() + original.Text = buf.String() addTab() }) } diff --git a/commands/msg/reply.go b/commands/msg/reply.go index a7379d7..2964a83 100644 --- a/commands/msg/reply.go +++ b/commands/msg/reply.go @@ -116,15 +116,16 @@ func (reply) Execute(aerc *widgets.Aerc, args []string) error { "Subject": subject, "In-Reply-To": msg.Envelope.MessageId, } + original := models.OriginalMail{} addTab := func() error { if template != "" { - defaults["OriginalFrom"] = models.FormatAddresses(msg.Envelope.From) - defaults["OriginalDate"] = msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM") + original.From = models.FormatAddresses(msg.Envelope.From) + original.Date = msg.Envelope.Date.Format("Mon Jan 2, 2006 at 3:04 PM") } composer, err := widgets.NewComposer(aerc, aerc.Config(), - acct.AccountConfig(), acct.Worker(), template, defaults) + acct.AccountConfig(), acct.Worker(), template, defaults, original) if err != nil { aerc.PushError("Error: " + err.Error()) return err @@ -155,7 +156,7 @@ func (reply) Execute(aerc *widgets.Aerc, args []string) error { store.FetchBodyPart(msg.Uid, msg.BodyStructure, []int{1}, func(reader io.Reader) { buf := new(bytes.Buffer) buf.ReadFrom(reader) - defaults["Original"] = buf.String() + original.Text = buf.String() addTab() }) return nil diff --git a/commands/msg/unsubscribe.go b/commands/msg/unsubscribe.go index 5ffec46..682b2b5 100644 --- a/commands/msg/unsubscribe.go +++ b/commands/msg/unsubscribe.go @@ -7,6 +7,7 @@ import ( "strings" "git.sr.ht/~sircmpwn/aerc/lib" + "git.sr.ht/~sircmpwn/aerc/models" "git.sr.ht/~sircmpwn/aerc/widgets" ) @@ -94,6 +95,7 @@ func unsubscribeMailto(aerc *widgets.Aerc, u *url.URL) error { acct.Worker(), "", defaults, + models.OriginalMail{}, ) if err != nil { return err diff --git a/lib/templates/template.go b/lib/templates/template.go index 21f7b35..8a345d9 100644 --- a/lib/templates/template.go +++ b/lib/templates/template.go @@ -10,6 +10,7 @@ import ( "text/template" "time" + "git.sr.ht/~sircmpwn/aerc/models" "github.com/mitchellh/go-homedir" ) @@ -28,20 +29,23 @@ type TemplateData struct { func TestTemplateData() TemplateData { defaults := map[string]string{ - "To": "John Doe <john@example.com>", - "Cc": "Josh Doe <josh@example.com>", - "From": "Jane Smith <jane@example.com>", - "Subject": "This is only a test", - "OriginalText": "This is only a test text", - "OriginalFrom": "John Doe <john@example.com>", - "OriginalDate": time.Now().Format("Mon Jan 2, 2006 at 3:04 PM"), + "To": "John Doe <john@example.com>", + "Cc": "Josh Doe <josh@example.com>", + "From": "Jane Smith <jane@example.com>", + "Subject": "This is only a test", } - return ParseTemplateData(defaults) + original := models.OriginalMail{ + Date: time.Now().Format("Mon Jan 2, 2006 at 3:04 PM"), + From: "John Doe <john@example.com>", + Text: "This is only a test text", + } + + return ParseTemplateData(defaults, original) } -func ParseTemplateData(defaults map[string]string) TemplateData { - originalDate, _ := time.Parse("Mon Jan 2, 2006 at 3:04 PM", defaults["OriginalDate"]) +func ParseTemplateData(defaults map[string]string, original models.OriginalMail) TemplateData { + originalDate, _ := time.Parse("Mon Jan 2, 2006 at 3:04 PM", original.Date) td := TemplateData{ To: parseAddressList(defaults["To"]), Cc: parseAddressList(defaults["Cc"]), @@ -49,8 +53,8 @@ func ParseTemplateData(defaults map[string]string) TemplateData { From: parseAddressList(defaults["From"]), Date: time.Now(), Subject: defaults["Subject"], - OriginalText: defaults["Original"], - OriginalFrom: parseAddressList(defaults["OriginalFrom"]), + OriginalText: original.Text, + OriginalFrom: parseAddressList(original.From), OriginalDate: originalDate, } return td diff --git a/models/models.go b/models/models.go index 036a609..7c3c192 100644 --- a/models/models.go +++ b/models/models.go @@ -164,3 +164,10 @@ func FormatAddresses(addrs []*Address) string { } return val.String() } + +// OriginalMail is helper struct used for reply/forward +type OriginalMail struct { + Date string + From string + Text string +} diff --git a/widgets/aerc.go b/widgets/aerc.go index da3f56f..a0e356a 100644 --- a/widgets/aerc.go +++ b/widgets/aerc.go @@ -14,6 +14,7 @@ import ( "git.sr.ht/~sircmpwn/aerc/config" "git.sr.ht/~sircmpwn/aerc/lib" "git.sr.ht/~sircmpwn/aerc/lib/ui" + "git.sr.ht/~sircmpwn/aerc/models" ) type Aerc struct { @@ -432,7 +433,7 @@ func (aerc *Aerc) Mailto(addr *url.URL) error { } } composer, err := NewComposer(aerc, aerc.Config(), - acct.AccountConfig(), acct.Worker(), "", defaults) + acct.AccountConfig(), acct.Worker(), "", defaults, models.OriginalMail{}) if err != nil { return nil } diff --git a/widgets/compose.go b/widgets/compose.go index 091eb70..636dcd1 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -26,6 +26,7 @@ import ( "git.sr.ht/~sircmpwn/aerc/config" "git.sr.ht/~sircmpwn/aerc/lib/templates" "git.sr.ht/~sircmpwn/aerc/lib/ui" + "git.sr.ht/~sircmpwn/aerc/models" "git.sr.ht/~sircmpwn/aerc/worker/types" ) @@ -59,7 +60,7 @@ type Composer struct { func NewComposer(aerc *Aerc, conf *config.AercConfig, acct *config.AccountConfig, worker *types.Worker, template string, - defaults map[string]string) (*Composer, error) { + defaults map[string]string, original models.OriginalMail) (*Composer, error) { if defaults == nil { defaults = make(map[string]string) @@ -68,7 +69,7 @@ func NewComposer(aerc *Aerc, conf *config.AercConfig, defaults["From"] = acct.From } - templateData := templates.ParseTemplateData(defaults) + templateData := templates.ParseTemplateData(defaults, original) cmpl := completer.New(conf.Compose.AddressBookCmd, func(err error) { aerc.PushError(fmt.Sprintf("could not complete header: %v", err)) worker.Logger.Printf("could not complete header: %v", err) -- 2.24.1
--- widgets/compose.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/widgets/compose.go b/widgets/compose.go index 636dcd1..9419579 100644 --- a/widgets/compose.go +++ b/widgets/compose.go @@ -488,8 +488,7 @@ func (c *Composer) PrepareHeader() (*mail.Header, []string, error) { // Merge in additional headers txthdr := mhdr.Header for key, value := range c.defaults { - // skip all Original* defaults, they contain info about original message - if !txthdr.Has(key) && value != "" && !strings.HasPrefix(key, "Original") { + if !txthdr.Has(key) && value != "" { mhdr.SetText(key, value) } } -- 2.24.1
--- commands/msg/reply.go | 6 ++++++ doc/aerc-templates.7.scd | 6 ++++++ lib/templates/template.go | 33 ++++++++++++++++++--------------- models/models.go | 7 ++++--- 4 files changed, 34 insertions(+), 18 deletions(-) diff --git a/commands/msg/reply.go b/commands/msg/reply.go index 2964a83..6b5a698 100644 --- a/commands/msg/reply.go +++ b/commands/msg/reply.go @@ -157,6 +157,12 @@ func (reply) Execute(aerc *widgets.Aerc, args []string) error { buf := new(bytes.Buffer) buf.ReadFrom(reader) original.Text = buf.String() + if len(msg.BodyStructure.Parts) == 0 { + original.MIMEType = fmt.Sprintf("%s/%s", msg.BodyStructure.MIMEType, msg.BodyStructure.MIMESubType) + } else { + // TODO: still will be "multipart/mixed" for mixed mails with attachments, fix this after aerc could handle responding to such mails + original.MIMEType = fmt.Sprintf("%s/%s", msg.BodyStructure.Parts[0].MIMEType, msg.BodyStructure.Parts[0].MIMESubType) + } addTab() }) return nil diff --git a/doc/aerc-templates.7.scd b/doc/aerc-templates.7.scd index 3c8b123..9382f2e 100644 --- a/doc/aerc-templates.7.scd +++ b/doc/aerc-templates.7.scd @@ -60,6 +60,12 @@ available always. Example: {{.Subject}} +*MIME Type* + MIME Type is available for quoted reply. + + - OriginalMIMEType: MIME type info of quoted mail part. Usually + "text/plain" or "text/html". + *Original Message* When using quoted reply or forward, the original message is available. It can be used using two functions that are available to templates. diff --git a/lib/templates/template.go b/lib/templates/template.go index 8a345d9..6eae5a2 100644 --- a/lib/templates/template.go +++ b/lib/templates/template.go @@ -22,9 +22,10 @@ type TemplateData struct { Date time.Time Subject string // Only available when replying with a quote - OriginalText string - OriginalFrom []*mail.Address - OriginalDate time.Time + OriginalText string + OriginalFrom []*mail.Address + OriginalDate time.Time + OriginalMIMEType string } func TestTemplateData() TemplateData { @@ -36,9 +37,10 @@ func TestTemplateData() TemplateData { } original := models.OriginalMail{ - Date: time.Now().Format("Mon Jan 2, 2006 at 3:04 PM"), - From: "John Doe <john@example.com>", - Text: "This is only a test text", + Date: time.Now().Format("Mon Jan 2, 2006 at 3:04 PM"), + From: "John Doe <john@example.com>", + Text: "This is only a test text", + MIMEType: "text/plain", } return ParseTemplateData(defaults, original) @@ -47,15 +49,16 @@ func TestTemplateData() TemplateData { func ParseTemplateData(defaults map[string]string, original models.OriginalMail) TemplateData { originalDate, _ := time.Parse("Mon Jan 2, 2006 at 3:04 PM", original.Date) td := TemplateData{ - To: parseAddressList(defaults["To"]), - Cc: parseAddressList(defaults["Cc"]), - Bcc: parseAddressList(defaults["Bcc"]), - From: parseAddressList(defaults["From"]), - Date: time.Now(), - Subject: defaults["Subject"], - OriginalText: original.Text, - OriginalFrom: parseAddressList(original.From), - OriginalDate: originalDate, + To: parseAddressList(defaults["To"]), + Cc: parseAddressList(defaults["Cc"]), + Bcc: parseAddressList(defaults["Bcc"]), + From: parseAddressList(defaults["From"]), + Date: time.Now(), + Subject: defaults["Subject"], + OriginalText: original.Text, + OriginalFrom: parseAddressList(original.From), + OriginalDate: originalDate, + OriginalMIMEType: original.MIMEType, } return td } diff --git a/models/models.go b/models/models.go index 7c3c192..8d254a1 100644 --- a/models/models.go +++ b/models/models.go @@ -167,7 +167,8 @@ func FormatAddresses(addrs []*Address) string { // OriginalMail is helper struct used for reply/forward type OriginalMail struct { - Date string - From string - Text string + Date string + From string + Text string + MIMEType string } -- 2.24.1
--- lib/templates/template.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/templates/template.go b/lib/templates/template.go index 6eae5a2..5402472 100644 --- a/lib/templates/template.go +++ b/lib/templates/template.go @@ -97,6 +97,7 @@ func wrapLine(text string, lineWidth int) string { func wrapText(text string, lineWidth int) string { text = strings.ReplaceAll(text, "\r\n", "\n") + text = strings.TrimRight(text, "\n") lines := strings.Split(text, "\n") var wrapped strings.Builder @@ -118,6 +119,7 @@ func wrapText(text string, lineWidth int) string { // quote prepends "> " in front of every line in text func quote(text string) string { text = strings.ReplaceAll(text, "\r\n", "\n") + text = strings.TrimRight(text, "\n") lines := strings.Split(text, "\n") var quoted strings.Builder for _, line := range lines { -- 2.24.1
--- doc/aerc-templates.7.scd | 21 +++++++++++++++++---- lib/templates/template.go | 23 +++++++++++++++++++++++ 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/doc/aerc-templates.7.scd b/doc/aerc-templates.7.scd index 9382f2e..adcc85c 100644 --- a/doc/aerc-templates.7.scd +++ b/doc/aerc-templates.7.scd @@ -72,20 +72,33 @@ available always. Example: - _wrapText_ function can be used to wrap the original text to a number + _wrap_ function can be used to wrap the original text to a number of characters per line. ``` - {{wrapText .OriginalText 72}} + {{wrap 72 .OriginalText}} ``` _quote_ function prepends each line with "> ". ``` {{quote .OriginalText}} ``` + _exec_ function execute external command to process message. + ``` + {{exec `/usr/local/share/aerc/filters/html`}} + ``` + + All of the above can be chained together if needed, for example. + ``` + {{exec `/usr/local/share/aerc/filters/html` .OriginalText | wrap 72 | quote}} + ``` - All of the above can be chained together if needed, for example + Automatic HTML parsing can be achieved. ``` - {{wrapText .OriginalText 72 | quote}} + {{if eq .OriginalMIMEType "text/html"}} + {{exec `/usr/local/share/aerc/filters/html` .OriginalText | wrap 72 | quote}} + {{else}} + {{wrap 72 .OriginalText | quote}} + {{end}} ``` # SEE ALSO diff --git a/lib/templates/template.go b/lib/templates/template.go index 5402472..f2765e8 100644 --- a/lib/templates/template.go +++ b/lib/templates/template.go @@ -5,6 +5,7 @@ import ( "errors" "net/mail" "os" + "os/exec" "path" "strings" "text/template" @@ -72,6 +73,11 @@ func parseAddressList(list string) []*mail.Address { return addrs } +// wrap allows to chain wrapText +func wrap(lineWidth int, text string) string { + return wrapText(text, lineWidth) +} + func wrapLine(text string, lineWidth int) string { words := strings.Fields(text) if len(words) == 0 { @@ -135,10 +141,27 @@ func quote(text string) string { return quoted.String() } +// cmd allow to parse reply by shell command +// text have to be passed by cmd param +// if there is error, original string is returned +func cmd(cmd, text string) string { + var out bytes.Buffer + c := exec.Command("sh", "-c", cmd) + c.Stdin = strings.NewReader(text) + c.Stdout = &out + err := c.Run() + if err != nil { + return text + } + return out.String() +} + var templateFuncs = template.FuncMap{ "quote": quote, "wrapText": wrapText, + "wrap": wrap, "dateFormat": time.Time.Format, + "exec": cmd, } func findTemplate(templateName string, templateDirs []string) (string, error) { -- 2.24.1
--- doc/aerc-templates.7.scd | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/aerc-templates.7.scd b/doc/aerc-templates.7.scd index adcc85c..a681fb2 100644 --- a/doc/aerc-templates.7.scd +++ b/doc/aerc-templates.7.scd @@ -32,7 +32,7 @@ available always. {{(index .From 0).Name}} ``` - Get the email address of the first sender + Get the email address of the first sender. ``` {{(index .From 0).Address}} ``` @@ -82,6 +82,7 @@ available always. ``` {{quote .OriginalText}} ``` + _exec_ function execute external command to process message. ``` {{exec `/usr/local/share/aerc/filters/html`}} -- 2.24.1
Fixed lines >80 columsn and pushed: To git.sr.ht:~sircmpwn/aerc da6fb1a..a40959c master -> master Thank you! This is a much better approach.