From 5df1ebce35bd8e5f98ca44d7ce8f2ffc0fe99d33 Mon Sep 17 00:00:00 2001
From: Fabio Manganiello <info@fabiomanganiello.com>
Date: Sun, 6 Nov 2022 01:22:52 +0100
Subject: [PATCH] Merged patch from
 https://lists.sr.ht/~rjarry/aerc-devel/patches/34390

Reference: https://lists.sr.ht/~rjarry/aerc-devel/patches/34390
---
 commands/compose/markup.go | 36 ++++++++++++++++++++++++++++++
 config/config.go           | 17 ++++++++++++++
 doc/aerc-config.5.scd      | 16 ++++++++++++++
 doc/aerc.1.scd             |  6 +++++
 widgets/compose.go         | 45 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 120 insertions(+)
 create mode 100644 commands/compose/markup.go

diff --git a/commands/compose/markup.go b/commands/compose/markup.go
new file mode 100644
index 0000000..2f646c1
--- /dev/null
+++ b/commands/compose/markup.go
@@ -0,0 +1,36 @@
+package compose
+
+import (
+	"fmt"
+	"git.sr.ht/~rjarry/aerc/widgets"
+)
+
+type MarkUp struct{}
+
+func init() {
+	register(MarkUp{})
+}
+
+func (MarkUp) Aliases() []string {
+	return []string{"markup"}
+}
+
+func (MarkUp) Complete(aerc *widgets.Aerc, args []string) []string {
+	composer, _ := aerc.SelectedTabContent().(*widgets.Composer)
+	return composer.GetMarkups()
+}
+
+func (MarkUp) Execute(aerc *widgets.Aerc, args []string) error {
+	if len(args) != 2 {
+		return fmt.Errorf("Usage: :markup <label>")
+	}
+	markupKey := args[1]
+	composer, _ := aerc.SelectedTabContent().(*widgets.Composer)
+	if err := composer.GenerateMarkup(markupKey); err != nil {
+		aerc.PushError(err.Error())
+		return err
+	} else {
+		aerc.PushSuccess(fmt.Sprintf("Markup generated"))
+	}
+	return nil
+}
diff --git a/config/config.go b/config/config.go
index a15e1d5..adcbeef 100644
--- a/config/config.go
+++ b/config/config.go
@@ -253,6 +253,11 @@ type TemplateConfig struct {
 	Forwards     string   `ini:"forwards"`
 }
 
+type MarkupConfig struct {
+	Name string
+	Cmd  string
+}
+
 type AercConfig struct {
 	Bindings        BindingConfig
 	ContextualBinds []BindingConfigContext
@@ -267,6 +272,7 @@ type AercConfig struct {
 	ContextualUis   []UIConfigContext
 	General         GeneralConfig
 	Templates       TemplateConfig
+    Markups         []MarkupConfig
 	Openers         map[string][]string
 }
 
@@ -616,6 +622,17 @@ func (config *AercConfig) LoadConfig(file *ini.File) error {
 		}
 	}
 
+	if markupsSec, err := file.GetSection("markups"); err == nil {
+		for _, markupKey := range markupsSec.KeyStrings() {
+			markupCmd := markupsSec.KeysHash()[markupKey]
+			mark := MarkupConfig{
+				Name: markupKey,
+				Cmd:  markupCmd,
+			}
+			config.Markups = append(config.Markups, mark)
+		}
+	}
+
 	// append default paths to template-dirs and styleset-dirs
 	for _, dir := range SearchDirs {
 		config.Ui.StyleSetDirs = append(
diff --git a/doc/aerc-config.5.scd b/doc/aerc-config.5.scd
index 94dfb59..fba9e9a 100644
--- a/doc/aerc-config.5.scd
+++ b/doc/aerc-config.5.scd
@@ -602,6 +602,22 @@ text/plain=gvim {} +125
 message/rfc822=thunderbird
 ```
 
+## MARKUPS
+
+Markups allows you to pipe the content of a message being composed through a
+shell command to generate HTML markup from the plaintext message. Markups
+can be defined in *[markups]* section within aerc.conf.
+
+Example:
+```
+[markups]
+work=pandoc -f markdown -t html --template=company.tpl
+```
+
+The command *:markup* becomes available during the compose-review stage, and
+will only append text/html part to the email if the shell command exits
+successfully and content is written to standard output.
+
 ## TRIGGERS
 
 Triggers specify commands to execute when certain events occur.
diff --git a/doc/aerc.1.scd b/doc/aerc.1.scd
index f3c0e52..b490e8b 100644
--- a/doc/aerc.1.scd
+++ b/doc/aerc.1.scd
@@ -500,6 +500,12 @@ message list, the message in the message viewer, etc).
 *edit*
 	(Re-) opens your text editor to edit the message in progress.
 
+*markup* <label>
+	Passes the message to external command, and appends result as text/html
+	message part. The label and command is defined under the *markups* section
+	configuration. For details on configuring markup support consult
+	*aerc-config*(5).
+
 *next-field*, *prev-field*
 	Cycles between input fields in the compose window.
 
diff --git a/widgets/compose.go b/widgets/compose.go
index 6e34365..245bd40 100644
--- a/widgets/compose.go
+++ b/widgets/compose.go
@@ -529,6 +529,51 @@ func (c *Composer) readSignatureFromFile() []byte {
 	return signature
 }
 
+func (c *Composer) GenerateMarkup(markupKey string) error {
+	markupCmd, err := c.getMarkupCmdByName(markupKey)
+	if err != nil {
+		return err
+	}
+	proc := exec.Command("sh", "-c", markupCmd)
+	stdin, err := proc.StdinPipe()
+	if err != nil {
+		return err
+	}
+	go func() {
+		defer stdin.Close()
+		io.Copy(stdin, c.email)
+	}()
+	outBytes, err := proc.Output()
+	if err != nil {
+		return err
+	}
+	if len(outBytes) < 1 {
+		return fmt.Errorf("markup %s did not generate content", markupKey)
+	}
+	ioReader := bytes.NewReader(outBytes)
+	return c.AppendPart("text/html", nil, ioReader)
+}
+
+func (c *Composer) GetMarkups() []string {
+	if len(c.config.Markups) < 1 {
+		return nil
+	}
+	markupKeys := []string{}
+	for _, cmd := range c.config.Markups {
+		markupKeys = append(markupKeys, cmd.Name)
+	}
+	return markupKeys
+}
+
+func (c *Composer) getMarkupCmdByName(markupKey string) (string, error) {
+	for _, markupConfig := range c.config.Markups {
+		if markupConfig.Name == markupKey {
+			return markupConfig.Cmd, nil
+		}
+	}
+	return "", fmt.Errorf("unknown markup label %s", markupKey)
+}
+
 func (c *Composer) FocusTerminal() *Composer {
 	c.Lock()
 	defer c.Unlock()