Email in the Terminal: Configuring Neomutt
Intro⌗
In one of my first posts about building an optimized workflow, I briefly mentioned neomutt was a program who’s configuration deserved a post all its own. This is that post.
Much like weechat, Neomutt is an extremely powerful tool but it needs a bit of customization to make it more “user friendly”.
On Arch we can install Neomutt with pacman -Syu neomutt
You have probably seen tons of tutorials about setting up offlineIMAP
or a similar service. I personally opt to connect directly to the IMAP server as its less of a hassle. Maybe
there is a good reason to do the whole offline sync thing but I haven’t found it.
Files⌗
You may have heard of a muttrc
or neomuttrc
. Like a vimrc
, these files define the settings for neomutt. I place my neomuttrc
in ~/.config/neomutt/
because its where most
programs store their config files.
However much like my custom neovim configuration, the main configuration file will simply be used to source the discrete configuration files containing specific configurations.
# vim: filetype=muttrc
source ~/.config/neomutt/settings
source ~/.config/neomutt/colors
source ~/.config/neomutt/mappings
# Accounts
source ~/.config/neomutt/accounts/gmail
folder-hook $folder 'source ~/.config/neomutt/accounts/gmail'
source ~/.config/neomutt/accounts/school
folder-hook $folder 'source ~/.config/neomutt/accounts/school'
source ~/.config/neomutt/accounts/main
folder-hook $folder 'source ~/.config/neomutt/accounts/main'
In the above neomuttrc
file, I source three files: settings
for general program configuration, colors
to adjust colors, and mappings
to store custom keybinds.
Additionally, in the accounts
folder, I have a configuration file for each of the email accounts I want to use with neomutt. We will get to that configuration later.
General Settings⌗
Here is where we can declare some universal settings for neomutt that change core program behavior.
# vim: filetype=muttrc
# set editor to neovim
set editor = "nvim"
set my_name = "Gideon Wolfe"
set imap_check_subscribed
# Set preffered view modes
auto_view text/html text/calendar application/ics # view html automatically
alternative_order text/html text/plain text/enriched text/*
# main options
set envelope_from
set edit_headers # show headers when composing
set fast_reply # skip to compose when replying
set askcc # ask for CC:
set fcc_attach # save attachments with the body
set forward_format = "Fwd: %s" # format of subject when forwarding
set forward_decode # decode when forwarding
set attribution = "On %d, %n wrote:" # format of quoting header
set reply_to # reply to Reply to: field
set reverse_name # reply as whomever it was to
set include # include message in replies
set forward_quote # include message in forwards
set text_flowed
unset sig_dashes # no dashes before sig
unset mime_forward # forward attachments as part of body
unset help # No help bar at the top of index
# set status_on_top # Status bar on top of index
set tmpdir = ~/Programs/neomutt/temp # where to keep temp files
unset confirmappend # don't ask, just do!
set quit # don't ask, just do!!
unset mark_old # read/new is good enough for me
set beep_new # bell on new mails
set pipe_decode # strip headers and eval mimes when piping
set thorough_search # strip headers and eval mimes before searching
set timeout = 0
# status bar, date format, finding stuff etc.
set status_chars = " *%A"
set status_format = "[ Folder: %f ] [%r%m messages%?n? (%n new)?%?d? (%d to delete)?%?t? (%t tagged)? ]%>─%?p?( %p postponed )?"
set date_format = "%d.%m.%Y %H:%M"
set sort = threads
set sort_aux = reverse-last-date-received
set uncollapse_jump
set sort_re
set reply_regexp = "^(([Rr][Ee]?(\[[0-9]+\])?: *)?(\[[^]]+\] *)?)*"
set quote_regexp = "^( {0,4}[>|:#%]| {0,4}[a-z0-9]+[>|]+)+"
set send_charset = "utf-8:iso-8859-1:us-ascii"
set charset = "utf-8"
set arrow_cursor = "no" # Change `color indicator` depending
# Pager View Options
set pager_index_lines = 10 # Shows 10 lines of index when pager is active
set pager_context = 3
set pager_stop
set menu_scroll
set tilde
unset markers
set mailcap_path = ~/.config/neomutt/mailcap
set header_cache = "~/.cache/mutt"
set message_cachedir = "~/.cache/mutt"
set query_command = "khard email --parsable --search-in-source-files '%s'"
I’ll go over some of the important parts, and the things people might often want to change:
editor
is set to neovim, so I can write all my emails with vim.reaper!query_command
hooks in with the contact managerkhard
, which I wrote about here. Tab complete your contacts!help
is unset so the top bar disappears. Just press?
if you need to see keybindings.- Optionally set
status_on_top
to see the status bar on top. Personally I like it on the bottom, as that’s where weechat puts it. arrow_cursor
can be enabled to have an arrow point to the currently selected message. I prefer the line as it’s more space efficient.
If you’re curious about any configuration variable, search this page and you can find a description.
Colors⌗
This file contains the color settings for various elements within neomutt.
# vim: filetype=muttrc
# Header colors:
color header blue default ".*"
color header brightmagenta default "^(From)"
color header brightcyan default "^(Subject)"
color header brightwhite default "^(CC|BCC)"
mono bold bold
mono underline underline
mono indicator reverse
mono error bold
color normal default default
color indicator brightyellow default # currently selected message. default makes bar clear, disabled arrow to save space.
color sidebar_highlight red default
color sidebar_divider brightblack black
color sidebar_flagged red black
color sidebar_new green black
color normal brightyellow default
color error red default
color tilde black default
color message cyan default
color markers red white
color attachment white default
color search brightmagenta default
color status brightyellow black
color hdrdefault brightgreen default
color quoted green default
color quoted1 blue default
color quoted2 cyan default
color quoted3 yellow default
color quoted4 red default
color quoted5 brightred default
color signature brightgreen default
color bold black default
color underline black default
color normal default default
color body brightred default "[\-\.+_a-zA-Z0-9]+@[\-\.a-zA-Z0-9]+" # Email addresses
color body brightblue default "(https?|ftp)://[\-\.,/%~_:?&=\#a-zA-Z0-9]+" # URL
color body green default "\`[^\`]*\`" # Green text between ` and `
color body brightblue default "^# \.*" # Headings as bold blue
color body brightcyan default "^## \.*" # Subheadings as bold cyan
color body brightgreen default "^### \.*" # Subsubheadings as bold green
color body yellow default "^(\t| )*(-|\\*) \.*" # List items as yellow
color body brightcyan default "[;:][-o][)/(|]" # emoticons
color body brightcyan default "[;:][)(|]" # emoticons
color body brightcyan default "[ ][*][^*]*[*][ ]?" # more emoticon?
color body brightcyan default "[ ]?[*][^*]*[*][ ]" # more emoticon?
color body red default "(BAD signature)"
color body cyan default "(Good signature)"
color body brightblack default "^gpg: Good signature .*"
color body brightyellow default "^gpg: "
color body brightyellow red "^gpg: BAD signature from.*"
mono body bold "^gpg: Good signature"
mohttps://neomutt.org/code/config_vars.htmlno body bold "^gpg: BAD signature from.*"
color body red default "([a-z][a-z0-9+-]*://(((([a-z0-9_.!~*'();:&=+$,-]|%[0-9a-f][0-9a-f])*@)?((([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?|[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+)(:[0-9]+)?)|([a-z0-9_.!~*'()$,;:@&=+-]|%[0-9a-f][0-9a-f])+)(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*(/([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*(;([a-z0-9_.!~*'():@&=+$,-]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?(#([a-z0-9_.!~*'();/?:@&=+$,-]|%[0-9a-f][0-9a-f])*)?|(www|ftp)\\.(([a-z0-9]([a-z0-9-]*[a-z0-9])?)\\.)*([a-z]([a-z0-9-]*[a-z0-9])?)\\.?(:[0-9]+)?(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*(/([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*(;([-a-z0-9_.!~*'():@&=+$,]|%[0-9a-f][0-9a-f])*)*)*)?(\\?([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?(#([-a-z0-9_.!~*'();/?:@&=+$,]|%[0-9a-f][0-9a-f])*)?)[^].,:;!)? \t\r\n<>\"]"
# Default index colors:
color index yellow default '.*'
color index_author red default '.*'
color index_number blue default
color index_subject cyan default '.*'
# For new mail:
color index brightyellow black "~N"
color index_author brightred black "~N"
color index_subject brightcyan black "~N"
color progress black cyan
Here are some of the highlights (pun very intended):
color indicator
changes the color of the highlighted message line, or the arrow ifarrow_cursor
is enabled.- neomutt does colors by layering. For example you might assign one color to the whole
index
, and then manually add different colors for new mail, from address, etc. - The various
color body
settings assign specific colors to anything matching a given regular expression, from list items to emoticons.
These colors look good with my pywal generated themes, and are easy to read without being too hard on the eyes. Here is a reference for coloring the index page if you want to make any tweaks.
Mappings⌗
On any page in neomutt, press ?
to get a full list of defined mappings for that page.
neomutt assigns mappings on a view basis. For example you could have a specific set of keybinds for when you are viewing an inbox (the index
view)
and another set for actually reading a message (pager
view).
Many keybinds can be assigned to multiple views by listing the views we want to pick.
Here is the contents of my mappings
file:
# vim: filetype=muttrc
# General rebindings
bind attach <return> view-mailcap
bind attach l view-mailcap
bind editor <space> noop
bind pager c imap-fetch-mail
bind index G last-entry
bind index g noop
bind index gg first-entry
bind pager,attach h exit
bind pager j next-line
bind pager k previous-line
bind pager l view-attachments
bind index D delete-message
bind index U undelete-message
bind index L limit
bind index h noop
bind index l display-message
bind browser h goto-parent
bind browser l select-entry
bind pager,browser gg top-page
bind pager,browser G bottom-page
bind index,pager,browser d half-down
bind index,pager,browser u half-up
bind index,pager R group-reply
bind index \031 previous-undeleted # Mouse wheel
bind index \005 next-undeleted # Mouse wheel
bind pager \031 previous-line # Mouse wheel
bind pager \005 next-line # Mouse wheel
bind editor <Tab> complete-query
# sidebar mappings
bind index,pager \Ck sidebar-prev
bind index,pager \Cj sidebar-next
bind index,pager \Co sidebar-open
bind index,pager \Cp sidebar-prev-new
bind index,pager \Cn sidebar-next-new
bind index,pager B sidebar-toggle-visible
# global index and pager shortcuts
bind index,pager @ compose-to-sender
bind index,pager D purge-message
bind index <tab> sync-mailbox
bind index <space> collapse-thread
# Email completion bindings
bind editor <Tab> complete-query
bind editor ^T complete
# Press A to add contact to Khard address book
macro index,pager A \
"<pipe-message>khard add-email<return>" \
"add the sender email address to khard"
## Shortcuts
macro index,pager <f2> '<sync-mailbox><enter-command>source ~/.config/neomutt/accounts/gmail<enter><change-folder>!<enter>'
macro index,pager <f3> '<sync-mailbox><enter-command>source ~/.config/neomutt/accounts/main<enter><change-folder>!<enter>'
macro index,pager <f4> '<sync-mailbox><enter-command>source ~/.config/neomutt/accounts/school<enter><change-folder>!<enter>'
The main differences between my config and the default:
- vim keybinds!
k
andj
to go up and down, andl
andh
to move forwards and back pages. - Open the sidebar with
B
.Ctrl+j
andCtrl+k
to navigate,Ctrl+o
to open selection A
will attempt to create a new contact withkhard
.F2
,F3
, andF4
are used to switch between my three mailboxes
I put the important binds in a little cheatsheet table for myself!
Function | Keybind |
---|---|
Go to last entry | G |
Go to first entry | gg |
Page down | d |
Page up | u |
Delete message | D |
Delete thread | Ctrl+d |
Mark thread read | Ctrl+r |
Undelete message | U |
Limit/Filter | L |
Open message | l , Enter |
Group reply | R |
Reply | r |
Forward message | f |
New message | m |
View attachments | v |
Compose to Sender | @ |
Resend message | Esc+e |
Save message (to file) | s |
Toggle collapse thread | Space , Esc+v |
Toggle collapse all threads | Esc+V |
Add contact to Khard | A |
Jump to parent message | P |
Email PGP public key | Esc+k |
Pager⌗
Function | Keybind |
---|---|
Bottom of page | G |
Top of page | gg |
Next line | j |
Previous line | k |
View attachments | l |
Page down | d |
Page up | u |
Group reply | R ,g |
Reply | r |
Compose to Sender | @ |
Delete thread | Ctrl+d |
Mark thread read | Ctrl+r |
Mark thread new | N |
Sidebar⌗
Function | Keybind |
---|---|
Show sidebar | B |
Up | Ctrl+k |
Down | Ctrl+j |
Open | Ctrl+o |
Open next new | Ctrl+n |
Open previous new | Ctrl+p |
Mailcap⌗
This file defines which programs should handle various filetypes included in email attachments.
# MS Word documents
application/msword; ~/dotfiles/office/view-attachment.sh %s "-" '/Applications/LibreOffice.app'
application/vnd.ms-excel; ~/dotfiles/office/view-attachment.sh %s "-" '/Applications/LibreOffice.app'
application/vnd.openxmlformats-officedocument.presentationml.presentation; ~/dotfiles/office/view-attachment.sh %s "-" '/Applications/LibreOffice.app'
application/vnd.oasis.opendocument.text; ~/dotfiles/office/view-attachment.sh %s "-" '/Applications/LibreOffice.app'
# HTML
text/html; w3m -I %{charset} -T text/html; copiousoutput;
text/plain; nvim %s
#PDFs
application/pdf; /usr/bin/zathura %s pdf
#Images
image/png; /usr/bin/feh %s
image/jpeg; /usr/bin/feh %s
- I use
feh
as a lightweight image viewer - All MS office docs are handled by
LibreOffice
zathura
reads my PDFs (with style)HTML
is rendered as text withw3m
Accounts⌗
I won’t go over the specifics of each account here, because getting neomutt to actually talk to your mailserver is another matter entirely, for which there are dozens of tutorials.
Here is a generic template for hooking up an IMAP
account.
# vim: filetype=muttrc
unmailboxes *
# in/out mail servers
set realname = "Firstname Lastname"
set imap_pass = "SOME_PASSWORD"
set smtp_url = "smtps://you@somedomain.com@mailserver.com:465"
set smtp_pass = "SOME_PASSWORD"
set folder = "imaps://you@somedomain.com@mailserver.com:993"
set spoolfile = "+INBOX"
set from = "you@somedomain.com"
set envelope_from
set use_from = "yes"
set record = "+Sent"
set trash = "+Trash"
set postponed = "+Drafts"
set mail_check = 6
# Allow Mutt to open a new IMAP connection automatically.
unset imap_passive
# Keep the IMAP connection alive by polling intermittently (time in seconds).
set imap_keepalive = 300
## Hook -- IMPORTANT!
account-hook $folder "set imap_pass=SOME_PASSWORD"
unmailboxes *
hides the inboxes of accounts we are not currently viewing.account-hook
is the line that allows us to switch to this account with a keybind.
Conclusion⌗
And that’s basically it. If you have done everything correctly, you should be able to use neomutt to interact with multiple email accounts, switching with just a keypress.
The colors should look good, and the keybinds should feel natural to a vim user. I am constantly improving this setup so don’t hesitate to throw me some suggestions.