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.
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.
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.
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:
[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
:
#!/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
:
[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.
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:
[[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
.
The excellent 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
:
language-servers = ["harper-ls"]
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
:
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:
>
prefixed email quotes).Sign-offs are preserved by following a simple heuristic. A two-line sequence is considered to be a signoff if:
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 repository on sourcehut. To add
it to your mail workspace just requires changing one line in languages.toml
:
formatter = { command = "/path/to/format.py" }
auto-format = true
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!