ficd.sh/content/blog/email-in-helix.md
2025-07-02 16:02:49 -04:00

256 lines
8.1 KiB
Markdown

---
title: Writing Emails In Helix
date: 2025-05-29
---
This article is all about writing emails in Helix. Obviously, Helix isn't an
email client -- and it's lacking in a plugin system, so you can't really turn it
into one, either. And that's okay! You might be wondering, then, what exactly do
I mean by "writing emails in Helix"?
An email client needs to do a whole lot more than write text! It needs to
connect to your mail servers, show your messages, display and allow you to
interact with flags/labels, be able to **send** your email, etc. Only an insane
person would try to hamfist this functionality into a text editor (I'm looking
at you, Neovim plugin developers!).
Helix is **really** good at its _one_ job, and that's **editing text**. It is a
text editor, after all. As it turns out, _writing_ emails is, well, text
editing, which is Helix's forte. So why not take Helix's amazing writing
experience, and apply it to email? If you're willing to take the plunge, you may
be pleasantly surprised at how **joyful** writing email can actually be!
There are a few different ways to approach composing emails in Helix. This post
covers what worked best for me. That is, using Helix as a composer for the
`aerc` email client. It wasn't straightforward to figure out at first, but
all-in-all, the configuration isn't actually that complicated, so you should be
able to set it up in no time.
<!--toc:start-->
- [Aerc Integration](#aerc-integration)
- [Naive Approach](#naive-approach)
- [Helix Workspace](#helix-workspace)
- [Markdown Highlighting](#markdown-highlighting)
- [Spellcheck](#spellcheck)
- [Formatting](#formatting)
- [Conclusion](#conclusion)
<!--toc:end-->
## Aerc Integration
At the end of the day, an email is just a text file. An `.eml` file, to be
exact. So, you _could_ just open up a fresh buffer, manually type in your
headers, and go to town. When you're done, you can figure out _some_ way to
actually send that file (perhaps dragging and dropping it into your mail
client).
However, that's not really ideal. For starters, emails have a **lot** of
headers, and if you mess any of them up, your message might not get delivered.
You probably want to be able to, at the very least, **reply** to an email and
have its headers be auto-filled.
Therefore, some level of integration with an actual email client is desirable.
Let the client handle every _other_ aspect of reading and sending email, and
relegate **writing** to Helix. This is the approach I'll be covering.
## Naive Approach
The simplest approach should work with zero configuration. `aerc` uses your
`$EDITOR` to compose your emails by default; so you can get started using Helix
right away! However, this is missing two important features: **spellchecking**
and **formatting**. To that end, we have to figure out how to get Helix to load
a custom configuration for emails.
## Helix Workspace
As you may know, Helix gives you an option to specify _per-project_
configuration. This requires creating a `.helix` directory in the root of your
project, and filling it with `config.toml` and `languages.toml` files that
override your default config. _However_, there isn't currently a way to specify
_filetype-specific_ options; this is where we have to get creative.
I recommend creating a folder inside your `aerc` config called `helix-config`.
Its name doesn't actually matter, so you can call it whatever you want! This
directory will serve as our _workspace_. The basic idea is to have `aerc` open
email files with this directory set as the current working directory — which is
what induces Helix to load the workspace config.
Inside our new workspace, let's create the `.helix` directory and populate
`config.toml` with our preferences:
```toml
[editor]
text-width = 74
[editor.soft-wrap]
enable = true
wrap-at-text-width = true
```
The final step is the simplest. First, create a shell script inside
`helix-config`:
```bash
#!/bin/sh
cd ~/.config/aerc/helix-config && hx "$@"
```
This will serve as our "email entry point". It simply sets the CWD to our new
workspace, then loads Helix normally. Then, you'll need to set the `editor`
option in `aerc.conf`:
```ini
[compose]
editor=/home/yourUserName/.config/aerc/helix-config/hx.sh
```
Unfortunately, you have to provide an absolute path, otherwise `aerc` won't find
the script.
## Markdown Highlighting
When writing plain-text emails, I like to use Markdown markup. Consider the
following message:
```
Dear John,
Thanks for sharing! I _seriously_ appreciate your help.
To proceed, we'll need to do:
- Thing A
- Thing B
```
Even though the recipient will receive this email in un-rendered plain text
format, the _semantics_ are still very clear. Of course, we don't _need_
highlighting in our editor to write markdown, but it can help make the
experience a bit better.
All you need to do is create a `.helix/languages.toml` file inside our email
workspace from before:
```toml
[[language]]
name = "markdown"
file-types = [{ glob = "/tmp/aerc-compose-*.eml" }]
```
Take note of the `file-types` entry. Email compose files opened by `aerc` will
always match the `/tmp/aerc-compose-*.eml` pattern. The above snippet ensures
that emails will be treated as Markdown files by Helix. The reason I recommend
putting this in your _workspace_ config and not setting it globally is to avoid
complications if you ever find yourself needing to edit a regular `.eml` file
_outside_ of `aerc`.
## Spellcheck
The excellent [harper](https://github.com/Automattic/harper) LSP supports
Markdown out-of-the-box. You can follow the instructions in its documentation to
install and configure it for Helix. Then, you can add it to your workspace
`languages.toml`:
```toml
language-servers = ["harper-ls"]
```
## Formatting
Many plain-text email clients _don't_ automatically wrap lines for readers. A
soft-wrapped email may look completely fine on your end, but be totally
unreadable for your recipient. Therefore, we need a way to _hard-wrap_ our
emails before sending them. This ended up being a much bigger challenge than I
thought it would.
My first approach was to use the nifty `wrap` filter that `aerc` ships with by
adding it to `languages.toml`:
```toml
formatter = { command = "/usr/lib/aerc/filters/wrap", args = ["-w", "74"] }
```
This worked _okay_, but it totally broke the markup at times. Crucially, it
doesn't respect code blocks or verbatim sections denoted by backticks. If you're
participating in technical mailing lists, you definitely want these to be
preserved.
Using a regular Markdown formatter introduced another problem: Markup was
preserved, but not in an email-friendly way. Consider this example:
```
How's it going?
Best,
Daniel
```
A standard Markdown formatter ignores single line breaks and produces this
output:
```
How's it going?
Best, Daniel
```
It similarly mangles signature blocks:
```
--
Daniel
Programmer
email@address.com
```
Becomes:
```
-- Daniel Programmer email@address.com
```
Eventually, I realized that I'd have to write my **own** formatter. I wrote a
Python script that hard-wraps the input, automatically reflows paragraphs, and
squashes consecutive paragraph breaks — while preserving the following:
- Any long sequence not broken by spaces.
- Quoted lines (`>` prefixed email quotes).
- Indented lines.
- Markdown lists.
- Markdown code blocks.
- Signature blocks at end of file.
- Sign-offs.
Sign-offs are preserved by following a simple heuristic. A two-line sequence is
considered to be a signoff if:
- It starts with 1-5 words ending with a comma, followed by
- 1-5 words that each start with capital letters.
This covers signoffs like these:
```
Best,
Daniel
Yours truly,
John Alfred Smith
```
I provide a number of flags and options, so you can configure the formatter to
your liking. If you want to use it, `format.py` is available in my
[mail-utils](https://git.sr.ht/~ficd/mail-utils) repository on sourcehut. To add
it to your mail workspace just requires changing one line in `languages.toml`:
```toml
formatter = { command = "/path/to/format.py" }
auto-format = true
```
## Conclusion
Hopefully, this guide was helpful in getting you set up with composing your
emails in Helix. A similar guide for Kakoune is in the works, so check back
soon!