--- title: Writing Emails In Kakoune date: 2025-06-01 --- This post will guide you through my setup for using Kakoune as an email composer inside `aerc`. I'll also explain how to configure Kakoune to act as the _pager_ for reading `text/plain` emails. If you only care about the final config, feel free to skip to it [here](#final-configuration). - [Naive Approach](#naive-approach) - [Composer Setup](#composer-setup) - [Reader Setup](#reader-setup) - [Final Configuration](#final-configuration) ## Naive Approach Since `aerc` uses your `$EDITOR` for composition, you don't technically have to do anything. I prefer setting it explicitly in `aerc.conf`, for good measure: ```ini [compose] editor=kak ``` The rest of the magic happens in your `kakrc`. ## Composer Setup Essentially, we want to hook `filetype=mail` and set our buffer configuration there. I'll share a recommended configuration with some explanation. ```kak hook global WinSetOption filetype=mail %~ set-option window formatcmd '/home/fic/dev/utils/mail-utils/format.py' set-option window comment_line '>' try autospell-enable hook -group mail-auto-format window BufWritePre .* format hook -once -always window WinSetOption filetype=.* %{ unset-option window formatcmd remove-hooks window mail-auto-format } ~ ``` I use a custom formatter to format emails. It automatically hard-wraps lines while preserving certain markup elements, code blocks, sign-offs, and signature blocks. For more details, check the formatting section of my post on [Helix](/blog/email/helix#formatting). I find that setting `>` as the `comment_line` token is convenient for working with quotes in replies. The `try autospell-enable` enables my [kak-autospell](https://git.sr.ht/~ficd/kak-autospell) plugin for the buffer. Essentially, it provides spellchecking that's continuously refreshed and hidden in insert mode. The remaining commands configure auto-formatting on save. I always prefer having this on so I never forget to format my message before sending it. ## Reader Setup I find that using Kakoune to **read** emails is helpful because of how easy it is to copy quotes, open links, etc. Configuring this is a tad hackier, however. The basic idea is to set Kakoune as the viewer `pager` in `aerc.conf`. However, all this does is pipe the email to `kak` through standard input, so we need to tell the editor to treat it like an email: ```ini [viewer] pager=kak -e 'set buffer filetype mail' ``` When you're using Kakoune as a pager, you'll probably want to configure some things differently. In my case, I like to set the buffer as `readonly`, remove the `number-lines` and `show-whitespaces` highlighters, disable soft-wrap & my scrolloff settings, and _not_ set any formatters. The `pager` command above sets the filetype, but we need to distinguish between _composing_ and _reading_ in our Kakoune hook. When Kakoune is opened with input through standard input, it loads a buffer that's conveniently named `*stdin*`. Thus, we can check the buffer name before continuing. If we're in "reading mode", we define a hidden command called `ismailreader` which doesn't do anything. Why? If the command is defined, and we try to invoke it... well, nothing happens! But if it's **not** defined, we get an error instead. We can combine this with the `try` command to for some simple boolean logic. ```kak evaluate-commands %sh{ # stdin, we assume it's a pager if [ "$kak_bufname" = "*stdin*" ]; then echo 'define-command -hidden ismailreader nop' fi } try %{ ismailreader # do reader config here } catch %{ # do composer config here } ``` I set the following configuration for "reader mode": ```kak set buffer readonly true try %{ remove-highlighter window/number-lines remove-highlighter window/show-whitespaces # custom commands defined elsewhere in my kakrc ui-wrap-disable ui-scrolloff-disable } ``` ## Final Configuration To recap, you'll want to set this in `aerc.conf`: ```ini [viewer] pager=kak -e 'set buffer filetype mail' # ... [compose] editor=kak ``` And the following in your `kakrc`: ```kak hook global WinSetOption filetype=mail %~ evaluate-commands %sh{ # stdin, we assume it's a pager if [ "$kak_bufname" = "*stdin*" ]; then echo 'define-command -hidden ismailreader nop' fi } try %{ # READER MODE setup ismailreader set buffer readonly true try %{ # remove these highlighters so everything displays properly remove-highlighter window/number-lines remove-highlighter window/show-whitespaces ui-wrap-disable ui-scrolloff-disable } } catch %{ # WRITER MODE setup set-option window formatcmd '/home/fic/dev/utils/mail-utils/format.py' set-option window comment_line '>' try autospell-enable hook -group mail-auto-format window BufWritePre .* format hook -once -always window WinSetOption filetype=.* %{ unset-option window formatcmd remove-hooks window mail-auto-format } } ~ ```