diff --git a/README.md b/README.md
index 183538b..e09b662 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,26 @@
-# Mail Format
+
Mail Format
+
+`mailfmt` is a simple plain text email formatter. It's designed to ensure
+consistent paragraph spacing while preserving markdown syntax, email headers,
+sign-offs, and signature blocks.
By default, this script accepts its input on `stdin` and prints to `stdout`.
-This makes it well suited for use with an editor like Helix. It has no
-dependencies besides the standard Python interpreter, and was written and tested
-against Python 3.13.2.
+This makes it well suited for use as a formatter with a text editor like Kakoune
+or Helix. It has no dependencies besides the standard Python interpreter, and
+was written and tested against Python 3.13.3.
-**Features:**
+
+
+- [Features](#features)
+- [Usage](#usage)
+- [Output Example](#output-example)
+- [Markdown Safety](#markdown-safety)
+- [Aerc Integration](#aerc-integration)
+- [Contributing](#contributing)
+
+
+
+## Features
- Wraps emails at specified columns.
- Automatically reflows paragraphs.
@@ -18,12 +33,16 @@ against Python 3.13.2.
- Markdown-style code blocks.
- Usenet-style signature block at EOF.
- Sign-offs.
+- If specified, output can be made safe for passing to a Markdown renderer.
+ - Use case: piping the output to `pandoc` to write a `text/html` message. See
+ [Markdown Safety](#markdown-safety).
-**Usage:**
+## Usage
```
-usage: format.py [-h] [-w WIDTH] [-b] [--no-replace-whitespace] [--no-reflow]
- [--no-signoff] [--no-signature] [--no-squash] [-i INPUT] [-o OUTPUT]
+usage: mailfmt [-h] [-w WIDTH] [-b] [--no-replace-whitespace] [--no-reflow]
+ [--no-signoff] [--no-signature] [--no-squash] [-m] [-i INPUT]
+ [-o OUTPUT]
Formatter for plain text email.
"--no-*" options are NOT passed by default.
@@ -39,6 +58,118 @@ options:
--no-signoff Don't preserve signoff line breaks.
--no-signature Don't preserve signature block.
--no-squash Don't squash consecutive paragraph breaks.
+ -m, --markdown-safe Output format safe for Markdown rendering.
-i, --input INPUT Input file. (default: STDIN)
-o, --output OUTPUT Output file. (default: STDOUT)
+
+Author : Daniel Fichtinger
+License: ISC
+Contact: daniel@ficd.ca
```
+
+## Output Example
+
+Before:
+
+```
+Hey,
+
+This is a really long paragraph with lots of words in it. However, my text editor uses soft-wrapping, so it ends up looking horrible when viewed without wrapping! Additionally,
+if I manually add some line breaks, things start to look _super_ janky!
+
+I can't just pipe this to `fmt` because it may break my beautiful
+markdown
+syntax. Markdown formatters are also problematic because they mess up
+my signoff and signature blocks! What should I do?
+
+Best wishes,
+Daniel
+
+--
+Daniel
+sr.ht/~ficd
+daniel@ficd.ca
+```
+
+After:
+
+```
+Hey,
+
+This is a really long paragraph with lots of words in it. However, my text
+editor uses soft-wrapping, so it ends up looking horrible when viewed
+without wrapping! Additionally, if I manually add some line breaks, things
+start to look _super_ janky!
+
+I can't just pipe this to `fmt` because it may break my beautiful markdown
+syntax. Markdown formatters are also problematic because they mess up my
+signoff and signature blocks! What should I do?
+
+Best wishes,
+Daniel
+
+--
+Daniel
+sr.ht/~ficd
+daniel@ficd.ca
+```
+
+## Markdown Safety
+
+In some cases, you may want to generate an HTML email. Ideally, you'd want the
+HTML to be generated directly from the plain text message, and for _both_
+versions to be legible and have the same semantics.
+
+Although `mailfmt` was written with Markdown markup in mind, its intended output
+is still the `text/plain` format. If you pass its output directly to a Markdown
+renderer, line breaks in sign-offs and the signature block won't be preserved.
+
+If you invoke `mailfmt --markdown-safe`, then `\` characters will be appended to
+mark line breaks that would otherwise be squashed, making the output suitable
+for conversion into HTML. Here's an example of one such pipeline:
+
+```bash
+cat message.txt | mailfmt --markdown-safe | pandoc -f markdown -t html
+--standalone > message.html
+```
+
+Here's the earlier example message with markdown safe output:
+
+```
+Hey,
+
+This is a really long paragraph with lots of words in it. However, my text
+editor uses soft-wrapping, so it ends up looking horrible when viewed
+without wrapping! Additionally, if I manually add some line breaks, things
+start to look _super_ janky!
+
+I can't just pipe this to `fmt` because it may break my beautiful markdown
+syntax. Markdown formatters are also problematic because they mess up my
+signoff and signature blocks! What should I do?
+
+Best wishes, \
+Daniel \
+
+-- \
+Daniel \
+sr.ht/~ficd \
+daniel@ficd.ca \
+```
+
+## Aerc Integration
+
+For integration with `aerc`, consider adding the following to your `aerc.conf`:
+
+```ini
+[multipart-converters]
+text/html=mailfmt --markdown-safe | pandoc -f markdown -t html --standalone
+```
+
+When you're done writing your email, you can call the `:multipart text/html`
+command to generate a `multipart/alternative` message which includes _both_ your
+original `text/plain` _and_ the newly generated `text/html` content.
+
+## Contributing
+
+Please send patches, requests, and concerns to my
+[public inbox](https://lists.sr.ht/~ficd/public-inbox).
diff --git a/mailfmt.py b/mailfmt
similarity index 87%
rename from mailfmt.py
rename to mailfmt
index 39b32a8..e9e2bbf 100755
--- a/mailfmt.py
+++ b/mailfmt
@@ -1,5 +1,12 @@
#!/bin/env python
+# TODO: generate an HTML version from the markdown syntax
+# while preserving the signoff and signature block
+# How to do this:
+# - Go through and do regular formatting, but add line breaks
+# - at the end of preserved lines. Then pass to md -> html converter.
+# Most simply, there should be an option to output "markdown safe" text.
+
# Simple text-wrapping script for email.
# Preserves code blocks, quotes, and signature.
# Automatically joins and re-wraps paragraphs to
@@ -25,9 +32,15 @@ reflow = True
width = 74
break_long_words = False
replace_whitespace = True
+markdown_safe = False
+
+in_signoff = False
+in_signature = False
def pprint(string: str):
+ if markdown_safe and (in_signoff or in_signature) and string:
+ string += " \\"
if not squash:
print(string, file=out_stream)
else:
@@ -136,6 +149,13 @@ Contact: daniel@ficd.ca
help="Don't squash consecutive paragraph breaks.",
action="store_false",
)
+ parser.add_argument(
+ "-m",
+ "--markdown-safe",
+ required=False,
+ help="Output format safe for Markdown rendering.",
+ action="store_true",
+ )
parser.add_argument(
"-i",
"--input",
@@ -160,6 +180,7 @@ Contact: daniel@ficd.ca
squash = args.no_squash
replace_whitespace = args.no_replace_whitespace
break_long_words = args.break_long_words
+ markdown_safe = args.markdown_safe
if args.input == "STDIN":
reader = sys.stdin
@@ -174,16 +195,19 @@ Contact: daniel@ficd.ca
if should_check_signoff:
is_signoff = check_signoff(line)
if is_signoff:
+ in_signoff = True
if not signoff_cache:
signoff_cache = line
else:
pprint(signoff_cache)
pprint(line)
+ in_signoff = False
signoff_cache = ""
continue
elif not is_signoff and signoff_cache:
paragraph.append(signoff_cache)
signoff_cache = ""
+ in_signoff = False
if line.startswith("```"):
flush_paragraph()
skipping = not skipping
@@ -191,6 +215,7 @@ Contact: daniel@ficd.ca
elif should_check_signature and line == "--":
flush_paragraph()
skipping = True
+ in_signature = True
pprint("-- ")
elif not line or re.match(
r"^(\s+|-\s+|\+\s+|\*\s+|>\s*|#\s+|From:|To:|Cc:|Bcc:|Subject:|Reply-To:|In-Reply-To:|References:|Date:|Message-Id:|User-Agent:)",