Emacs Frontend for Notmuch

About this Manual

This manual covers only the Emacs interface to Notmuch. For information on the command line interface, see section “Description” in the Notmuch Manual Pages. To save typing, we will sometimes use notmuch in this manual to refer to the Emacs interface to Notmuch. When this distinction is important, we’ll refer to the Emacs interface as notmuch-emacs.

Notmuch-emacs is highly customizable via the the Emacs customization framework (or just by setting the appropriate variables). We try to point out relevant variables in this manual, but in order to avoid duplication of information, you can usually find the most detailed description in the variables’ docstring.

notmuch-hello

notmuch-hello is the main entry point for Notmuch. You can start it with M-x notmuch or M-x notmuch-hello. The startup screen looks something like the following. There are some hints at the bottom of the screen. There are three main parts to the notmuch-hello screen, discussed below. The bold text indicates buttons you can click with a mouse or by positioning the cursor and pressing <return>

Welcome to notmuch You have 52 messages.

Saved searches: [edit]

52 inbox 52 unread

Search: ____________________________________

All tags: [show]

Hit `?’ for context-sensitive help in any Notmuch screen.
Customize Notmuch or this page.

You can change the overall appearance of the notmuch-hello screen by customizing the variables

defcustom notmuch-hello-sections

Sections for notmuch-hello.

The list contains functions which are used to construct sections in notmuch-hello buffer. When notmuch-hello buffer is constructed, these functions are run in the order they appear in this list. Each function produces a section simply by adding content to the current buffer. A section should not end with an empty line, because a newline will be inserted after each section by notmuch-hello.

Each function should take no arguments. The return value is ignored.

For convenience an element can also be a list of the form (FUNC ARG1 ARG2 .. ARGN) in which case FUNC will be applied to the rest of the list.

A “Customized tag-list section” item in the customize-interface displays a list of all tags, optionally hiding some of them. It is also possible to filter the list of messages matching each tag by an additional filter query. Similarly, the count of messages displayed next to the buttons can be generated by applying a different filter to the tag query. These filters are also supported for “Customized queries section” items.

defcustom notmuch-hello-thousands-separator

The string used as a thousands separator.

Typically “,” in the US and UK and “.” or ” ” in Europe. The latter is recommended in the SI/ISO 31-0 standard and by the International Bureau of Weights and Measures.

Should the notmuch logo be shown?

defcustom notmuch-column-control

Controls the number of columns for saved searches/tags in notmuch view.

This variable has three potential types of values:

t

Automatically calculate the number of columns possible based on the tags to be shown and the window width.

integer <n>

A lower bound on the number of characters that will be used to display each column.

float <f>

A fraction of the window width that is the lower bound on the number of characters that should be used for each column.

So:

  • if you would like two columns of tags, set this to 0.5.

  • if you would like a single column of tags, set this to 1.0.

  • if you would like tags to be 30 characters wide, set this to 30.

  • if you don’t want to worry about all of this nonsense, leave this set to t.

defcustom notmuch-show-empty-saved-searches

Should saved searches with no messages be listed?

notmuch-hello key bindings

<tab>

Move to the next widget (button or text entry field)

<backtab>

Move to the previous widget.

<return>

Activate the current widget.

g
=

Refresh the buffer; mainly update the counts of messages for various saved searches.

G

Import mail, See Importing Mail

m

Compose a message

s

Search the notmuch database using notmuch-search

v

Print notmuch version

q

Quit

Saved Searches

Since notmuch is entirely search-based, it’s often useful to organize mail around common searches. To facilitate this, the first section of notmuch-hello presents a customizable set of saved searches. Saved searches can also be accessed from anywhere in notmuch by pressing j to access notmuch-jump.

The saved searches default to various common searches such as tag:inbox to access the inbox and tag:unread to access all unread mail, but there are several options for customization:

defcustom notmuch-saved-searches

The list of saved searches, including names, queries, and additional per-query options.

defcustom notmuch-saved-search-sort-function

This variable controls how saved searches should be sorted. A value of nil displays the saved searches in the order they are stored in ‘notmuch-saved-searches’.

Known Tags

One special kind of saved search provided by default is for each individual tag defined in the database. This can be controlled via the following variables.

defcustom notmuch-hello-tag-list-make-query

Control how to construct a search (“virtual folder”) from a given tag.

defcustom notmuch-hello-hide-tags

Which tags not to display at all.

notmuch-show

notmuch-show-mode is used to display a single thread of email from your email archives.

By default, various components of email messages, (citations, signatures, already-read messages), are hidden. You can make these parts visible by clicking with the mouse button or by pressing RET after positioning the cursor on a hidden part.

<space>

Scroll the current message (if necessary), advance to the next message, or advance to the next thread (if already on the last message of a thread).

c

Copy to kill-ring

N

Move to next message

P

Move to previous message (or start of current message)

n

Move to next matching message

p

Move to previous matching message

+
-

Add or remove arbitrary tags from the current message.

!

Toggle the display of non-matching messages.

?

Display full set of key bindings

Display of messages can be controlled by the following variables; see also Dealing with large messages and threads.

defcustom notmuch-message-headers

Headers that should be shown in a message, in this order.

For an open message, all of these headers will be made visible according to notmuch-message-headers-visible or can be toggled with notmuch-show-toggle-visibility-headers. For a closed message, only the first header in the list will be visible.

defcustom notmuch-message-headers-visible

Should the headers be visible by default?

If this value is non-nil, then all of the headers defined in notmuch-message-headers will be visible by default in the display of each message. Otherwise, these headers will be hidden and notmuch-show-toggle-visibility-headers can be used to make them visible for any given message.

defcustom notmuch-show-header-line

Show a header line in notmuch show buffers.

If t (the default), the header line will contain the current message’s subject.

If a string, this value is interpreted as a format string to be passed to format-spec with %s as the substitution variable for the message’s subject. E.g., to display the subject trimmed to a maximum of 80 columns, you could use “%>-80s” as format.

If you assign to this variable a function, it will be called with the subject as argument, and the return value will be used as the header line format. Since the function is called with the message buffer as the current buffer, it is also possible to access any other properties of the message, using for instance notmuch-show functions such as notmuch-show-get-message-properties.

Finally, if this variable is set to nil, no header is displayed.

defcustom notmuch-multipart/alternative-discouraged

Which mime types to hide by default for multipart messages.

Can either be a list of mime types (as strings) or a function mapping a plist representing the current message to such a list. The following example function would discourage text/html and multipart/related generally, but discourage text/plain should the message be sent from whatever@example.com.

(defun my--determine-discouraged (msg)
  (let* ((headers (plist-get msg :headers))
         (from (or (plist-get headers :From) "")))
    (cond
     ((string-match "whatever@example.com" from)
      (list "text/plain"))
     (t
      (list "text/html" "multipart/related")))))

Dealing with large messages and threads

If you are finding notmuch-show is annoyingly slow displaying large messages, you can customize notmuch-show-max-text-part-size. If you want to speed up the display of large threads (with or without large messages), there are several options. First, you can display the same query in one of the other modes. notmuch-unthreaded is the most robust for extremely large queries, but notmuch-tree is also be faster than notmuch-show in general, since it only renders a single message a time. If you prefer to stay with the rendered thread (“conversation”) view of notmuch-show, you can customize the variables notmuch-show-depth-limit, notmuch-show-height-limit and notmuch-show-max-text-part-size to limit the amount of rendering done initially. Note that these limits are implicitly OR-ed together, and combinations might have surprising effects.

defcustom notmuch-show-depth-limit

Depth beyond which message bodies are displayed lazily.

If bound to an integer, any message with tree depth greater than this will have its body display lazily, initially inserting only a button.

If this variable is set to nil (the default) no such lazy insertion is done.

defcustom notmuch-show-height-limit

Height (from leaves) beyond which message bodies are displayed lazily.

If bound to an integer, any message with height in the message tree greater than this will have its body displayed lazily, initially only a button.

If this variable is set to nil (the default) no such lazy display is done.

defcustom notmuch-show-max-text-part-size

Maximum size of a text part to be shown by default in characters.

Set to 0 to show the part regardless of size.

Copy to kill-ring

You can use the usually Emacs ways of copying text to the kill-ring, but notmuch also provides some shortcuts. These keys are available in notmuch-show, and notmuch-tree. A subset are available in notmuch-search.

c F
M-x notmuch-show-stash-filename

Copy filename of current message to kill-ring.

c G
M-x notmuch-show-stash-git-send-email

Copy From/To/Cc/Message-Id of current message to kill-ring. Use a form suitable for pasting to git send-email command line.

If invoked with a prefix argument (or NO-IN-REPLY-TO is non-nil), omit –in-reply-to=<Message-Id>.

c I
M-x notmuch-show-stash-message-id-stripped

Copy message ID of current message (sans id: prefix) to kill-ring.

c L

Copy an ML Archive URI for the current message to the kill-ring and visit it.

This presumes that the message is available at the selected Mailing List Archive.

If optional argument MLA is non-nil, use the provided key instead of prompting the user (see notmuch-show-stash-mlarchive-link-alist).

c T
M-x notmuch-show-stash-tags

Copy tags of current message to kill-ring as a comma separated list.

c c
M-x notmuch-show-stash-cc

Copy CC field of current message to kill-ring.

c d
M-x notmuch-show-stash-date

Copy date of current message to kill-ring.

If invoked with a prefix argument, copy timestamp of current message to kill-ring.

c f
M-x notmuch-show-stash-from

Copy From address of current message to kill-ring.

c i
M-x notmuch-show-stash-message-id

Copy id: query matching the current message to kill-ring.

If invoked with a prefix argument (or STASH-THREAD-ID is non-nil), copy thread: query matching the current thread to kill-ring.

c l

Copy an ML Archive URI for the current message to the kill-ring.

This presumes that the message is available at the selected Mailing List Archive.

If optional argument MLA is non-nil, use the provided key instead of prompting the user (see notmuch-show-stash-mlarchive-link-alist).

c s
M-x notmuch-show-stash-subject

Copy Subject field of current message to kill-ring.

c t
M-x notmuch-show-stash-to

Copy To address of current message to kill-ring.

c ?
M-x notmuch-subkeymap-help

Show all available copying commands

Dealing with duplicates

If there are are multiple files with the same Message-ID (see DUPLICATE MESSAGE FILES), then notmuch-show displays the number of duplicates and identifies the current duplicate. In the following example duplicate 3 of 5 is displayed.

 M. Mustermann <max@example.com> (Sat, 30 Jul 2022 10:33:10 -0300) (inbox signed)      3/5
 Subject: Re: Multiple files per message in emacs
 To: notmuch@notmuchmail.org
%
M-x notmuch-show-choose-duplicate

Display message file with index DUPLICATE in place of the current one.

Message file indices are based on the order the files are discovered by notmuch new (and hence are somewhat arbitrary), and correspond to those passed to the “--duplicate” arguments to the CLI.

When called interactively, the function will prompt for the index of the file to display. An error will be signaled if the index is out of range.

notmuch-tree

notmuch-tree-mode displays the results of a “notmuch tree” of your email archives. Each line in the buffer represents a single message giving the relative date, the author, subject, and any tags.

c

Copy to kill-ring

<return>

Displays that message.

N

Move to next message

P

Move to previous message

n

Move to next matching message

p

Move to previous matching message

o
M-x notmuch-tree-toggle-order

Toggle the current search order.

This command toggles the sort order for the current search. The default sort order is defined by notmuch-search-oldest-first.

l
M-x notmuch-tree-filter

Filter or LIMIT the current search results based on an additional query string

t
M-x notmuch-tree-filter-by-tag

Filter the current search results based on an additional tag

g
=

Refresh the buffer

?

Display full set of key bindings

As is the case with notmuch-search, the presentation of results can be controlled by the variable notmuch-search-oldest-first.

defcustom notmuch-tree-result-format

Result formatting for tree view.

List of pairs of (field . format-string). Supported field strings are: “date”, “authors”, “subject”, “tree”, “tags”. It is also supported to pass a function in place of a field-name. In this case the function is passed the thread object (plist) and format string.

Tree means the thread tree box graphics. The field may also be a list in which case the formatting rules are applied recursively and then the output of all the fields in the list is inserted according to format-string.

Note that the author string should not contain whitespace (put it in the neighbouring fields instead).

The following example shows how to optionally display recipients instead of authors for sent mail (assuming the user is named Mustermann).

(defun -notmuch-authors-or-to (format-string result)
  (let* ((headers (plist-get result :headers))
         (to (plist-get headers :To))
         (author (plist-get headers :From))
         (face (if (plist-get result :match)
                   'notmuch-tree-match-author-face
                 'notmuch-tree-no-match-author-face)))
    (propertize
     (format format-string
             (if (string-match "Mustermann" author)
                 (concat "To:" (notmuch-tree-clean-address to))
               author))
     'face face)))

(setq notmuch-tree-result-format
      '(("date" . "%12s  ")
        (-notmuch-authors-or-to . "%-20.20s")
        ((("tree" . "%s")
          ("subject" . "%s"))
         . " %-54s ")
        ("tags" . "(%s)")))

See also notmuch-search-result-format and notmuch-unthreaded-result-format.

notmuch-unthreaded

notmuch-unthreaded-mode is similar to notmuch-tree in that each line corresponds to a single message, but no thread information is presented.

Keybindings are the same as notmuch-tree.

defcustom notmuch-unthreaded-result-format

Result formatting for unthreaded tree view.

List of pairs of (field . format-string). Supported field strings are: “date”, “authors”, “subject”, “tree”, “tags”. It is also supported to pass a function in place of a field-name. In this case the function is passed the thread object (plist) and format string.

Tree means the thread tree box graphics. The field may also be a list in which case the formatting rules are applied recursively and then the output of all the fields in the list is inserted according to format-string.

Note that the author string should not contain whitespace (put it in the neighbouring fields instead).

See also notmuch-search-result-format and notmuch-tree-result-format.

Global key bindings

Several features are accessible from most places in notmuch through the following key bindings:

j

Jump to saved searches using notmuch-jump.

k

Tagging operations using notmuch-tag-jump

C-_
C-/
C-x u

Undo previous tagging operation using notmuch-tag-undo

notmuch-jump

Saved searches configured through Saved Searches can include a “shortcut key” that’s accessible through notmuch-jump. Pressing j anywhere in notmuch followed by the configured shortcut key of a saved search will immediately jump to that saved search. For example, in the default configuration j i jumps immediately to the inbox search. When you press j, notmuch-jump shows the saved searches and their shortcut keys in the mini-buffer.

notmuch-tag-jump

Tagging operations configured through notmuch-tagging-keys can be accessed via k in notmuch-show, notmuch-search and notmuch-tree. With a prefix (C-u k), notmuch displays a menu of the reverses of the operations specified in notmuch-tagging-keys; i.e. each +tag is replaced by -tag and vice versa.

defcustom notmuch-tagging-keys

A list of keys and corresponding tagging operations.

For each key (or key sequence) you can specify a sequence of tagging operations to apply, or a variable which contains a list of tagging operations such as notmuch-archive-tags. The final element is a name for this tagging operation. If the name is omitted or empty then the list of tag changes, or the variable name is used as the name.

The key notmuch-tag-jump-reverse-key (k by default) should not be used (either as a key, or as the start of a key sequence) as it is already bound: it switches the menu to a menu of the reverse tagging operations. The reverse of a tagging operation is the same list of individual tag-ops but with +tag replaced by -tag and vice versa.

If setting this variable outside of customize then it should be a list of triples (lists of three elements). Each triple should be of the form (key-binding tagging-operations name). KEY-BINDING can be a single character or a key sequence; TAGGING-OPERATIONS should either be a list of individual tag operations each of the form +tag or -tag, or the variable name of a variable that is a list of tagging operations; NAME should be a name for the tagging operation, if omitted or empty than then name is taken from TAGGING-OPERATIONS.

notmuch-tag-undo

Each notmuch buffer supporting tagging operations (i.e buffers in notmuch-show, notmuch-search, notmuch-tree, and notmuch-unthreaded mode) keeps a local stack of tagging operations. These can be undone via notmuch-tag-undo. By default this is bound to the usual Emacs keys for undo.

C-_
C-/
C-x u
M-x notmuch-tag-undo

Undo the previous tagging operation in the current buffer. Uses buffer local variable notmuch-tag-history to determine what that operation was.

Buffer navigation

M-x notmuch-cycle-notmuch-buffers

Cycle through any existing notmuch buffers (search, show or hello).

If the current buffer is the only notmuch buffer, bury it. If no notmuch buffers exist, run notmuch.

Configuration

Importing Mail

M-x notmuch-poll

Run “notmuch new” or an external script to import mail.

Invokes notmuch-poll-script, “notmuch new”, or does nothing depending on the value of notmuch-poll-script.

defcustom notmuch-poll-script

[Deprecated] Command to run to incorporate new mail into the notmuch database.

This option has been deprecated in favor of “notmuch new” hooks (see man notmuch-hooks). To change the path to the notmuch binary, customize notmuch-command.

This variable controls the action invoked by notmuch-poll-and-refresh-this-buffer (bound by default to ‘G’) to incorporate new mail into the notmuch database.

If set to nil (the default), new mail is processed by invoking “notmuch new”. Otherwise, this should be set to a string that gives the name of an external script that processes new mail. If set to the empty string, no command will be run.

The external script could do any of the following depending on the user’s needs:

1. Invoke a program to transfer mail to the local mail store 2. Invoke “notmuch new” to incorporate the new mail 3. Invoke one or more “notmuch tag” commands to classify the mail

Sending Mail

defcustom mail-user-agent

Emacs consults the variable mail-user-agent to choose a mail sending package for commands like report-emacs-bug and compose-mail. To use notmuch for this, customize this variable to the symbol notmuch-user-agent.

defcustom message-dont-reply-to-names

When composing mail replies, Emacs’s message mode uses the variable message-dont-reply-to-names to exclude recipients matching a given collection of regular expressions or satisfying an arbitrary predicate. Notmuch’s MUA inherits this standard mechanism and will honour your customization of this variable.

Init File

When Notmuch is loaded, it will read the notmuch-init-file (~/.emacs.d/notmuch-config by default) file. This is normal Emacs Lisp file and can be used to avoid cluttering your ~/.emacs with Notmuch stuff. If the file with .elc, .elc.gz, .el or .el.gz suffix exist it will be read instead (just one of these, chosen in this order). Most often users create ~/.emacs.d/notmuch-config.el and just work with it. If Emacs was invoked with the -q or --no-init-file options, notmuch-init-file is not read.