+[[!img notmuch-logo.png alt="Notmuch logo" class="left"]]
# Tips and Tricks for using notmuch with Emacs
One of the more popular notmuch message reading clients is
## Navigating & reading mails
When first starting notmuch in emacs, you will be presented with the
-notmuch "hello" page. From here you can do searches, see lists of
-recent searches, saved searches, message tags, help information, etc.
+notmuch "hello" page. If it exits with an error after writing
+"Welcome to notmutch. You have" you need to do the basic notmuch setup
+first (see above).
+From here you can do searches, see lists of recent
+searches, saved searches, message tags, help information, etc.
Executing a search will open a new buffer in `notmuch-search-mode`
displaying the search results. Each line in the search results
If notmuch-show-mode behaves badly for you in emacs 24.x try adding one of
- (setq gnus-inhibit-images nil)
+ (setq gnus-inhibit-images nil)
or
- (require 'gnus-art)
+ (require 'gnus-art)
to your .emacs file.
;;; my-notmuch.el -- my notmuch mail configuration
;;;
-
+
;;; add here stuff required to be configured *before*
;;; notmuch is loaded;
;; uncomment and modify in case some elisp files are not found in load-path
;; (add-to-list 'load-path "~/vc/ext/notmuch/emacs")
- ;;; load notmuch
+ ;;; load notmuch
(require 'notmuch)
;;; add here stuff required to be configured *after*
(autoload 'notmuch "~/.emacs.d/my-notmuch" "notmuch mail" t)
+## Initial cursor position in notmuch 0.15 hello window
-## Add a key binding to add/remove/toggle a tag
+In notmuch version 0.15 emacs client the handling of cursor position in
+notmuch hello window has been simplified to a version which suits best
+most cases.
-The `notmuch-{search,show}-{add,remove}-tag` functions are very useful
-for making quick tag key bindings. For instance, here's an example
-of how to make a key binding to add the "spam" tag and remove the
-"inbox" tag in notmuch-show-mode:
+Initially the cursor is positioned at the beginning of buffer.
-In notmuch versions up to 0.11.x
+Some users liked the "ancient" version where cursor was moved to the
+first `Saved searches` button.
- (define-key notmuch-show-mode-map "S"
- (lambda ()
- "mark message as spam"
- (interactive)
- (notmuch-show-add-tag "spam")
- (notmuch-show-remove-tag "inbox")))
+Add the following code to your notmuch emacs configuration file in
+case you want this behaviour:
-Starting from notmuch 0.12 the functions `notmuch-show-add-tag` and
-`notmuch-show-remove-tag` have changed to be more versatile and lost
-noninteractive use. When upgrading to 0.12 the above needs to be
-changed to this:
+ (add-hook 'notmuch-hello-refresh-hook
+ (lambda ()
+ (if (and (eq (point) (point-min))
+ (search-forward "Saved searches:" nil t))
+ (progn
+ (forward-line)
+ (widget-forward 1))
+ (if (eq (widget-type (widget-at)) 'editable-field)
+ (beginning-of-line)))))
- (define-key notmuch-show-mode-map "S"
- (lambda ()
- "mark message as spam"
- (interactive)
- (notmuch-show-tag-message "+spam" "-inbox")))
+## Add a key binding to add/remove/toggle a tag
-You can do the same for threads in `notmuch-search-mode` by just
-replacing "show" with "search" in the called functions.
+The `notmuch-{search,show,tree}-tag` functions are very useful for
+making quick tag key bindings. The arguments to these functions have
+changed as notmuch has evolved but the following should work on all
+versions of notmuch from 0.13 on. These functions take a list of
+tag changes as argument. For example, an argument of (list "+spam"
+"-inbox) adds the tag spam and deletes the tag inbox. Note the
+argument must be a list even if there is only a single tag change
+e.g., use (list "+deleted") to add the deleted tag.
-Starting from notmuch 0.12 use `notmuch-search-tag-thread` instead:
+For instance, here's an example of how to make a key binding to add
+the "spam" tag and remove the "inbox" tag in notmuch-show-mode:
- (define-key notmuch-search-mode-map "S"
+ (define-key notmuch-show-mode-map "S"
(lambda ()
- "mark messages in thread as spam"
+ "mark message as spam"
(interactive)
- (notmuch-show-tag-thread "+spam" "-inbox")))
-
-Starting from notmuch 0.13 use `notmuch-search-tag` -- it has a little
-different usage syntax:
+ (notmuch-show-tag (list "+spam" "-inbox"))))
- (define-key notmuch-search-mode-map "S"
- (lambda ()
- "mark messages in thread as spam"
- (interactive)
- (notmuch-search-tag '("+spam" "-inbox"))))
+You can do the same for threads in `notmuch-search-mode` by just
+replacing "show" with "search" in the keymap and called functions, or
+for messages in `notmuch-tree-mode` by replacing "show" by "tree". If
+you want to tag a whole thread in `notmuch-tree-mode` use
+`notmuch-tree-tag-thread` instead of `notmuch-tree-tag`.
-The definition above makes use of a lambda function, but you could
+The definitions above make use of a lambda function, but you could
also define a separate function first:
(defun notmuch-show-tag-spam ()
"mark message as spam"
(interactive)
- (notmuch-show-add-tag "spam")
- (notmuch-show-remove-tag "inbox")))
- (define-key notmuch-show-mode-map "S" 'notmuch-show-tag-spam)
+ (notmuch-show-add-tag (list "+spam" "-inbox")))
-(See above for analogy how to apply this for notmuch 0.12 and later)
+ (define-key notmuch-show-mode-map "S" 'notmuch-show-tag-spam)
Here's a more complicated example of how to add a toggle "deleted"
key:
"toggle deleted tag for message"
(interactive)
(if (member "deleted" (notmuch-show-get-tags))
- (notmuch-show-remove-tag "deleted")
- (notmuch-show-add-tag "deleted"))))
-
-And version for notmuch 0.12
-
- (define-key notmuch-show-mode-map "d"
- (lambda ()
- "toggle deleted tag for message"
- (interactive)
- (notmuch-show-tag-message
- (if (member "deleted" (notmuch-show-get-tags))
- "-deleted" "+deleted"))))
+ (notmuch-show-tag (list "-deleted"))
+ (notmuch-show-tag (list "+deleted")))))
## Adding many tagging keybindings
The Emacs interface to notmuch will automatically add an `Fcc`
header to your outgoing mail so that any messages you send will also
be saved in your mail store. You can control where this copy of the
-message is saved by setting the variables `message-directory` (which
-defines a base directory) and `notmuch-fcc-dirs` which defines the
-subdirectory relative to `message-directory` in which to save the
-mail. Enter a directory (without the maildir `/cur` ending which
-will be appended automatically). To customize both variables at the
-same time, use the fancy command:
+message is saved by setting the variable `notmuch-fcc-dirs` which defines the
+subdirectory relative to the `database.path` setting from your
+notmuch configuration in which to save the mail. Enter a directory
+(without the maildir `/cur` ending which will be appended
+automatically). Additional information can be found as usual using:
+
+ M-x describe-variable notmuch-fcc-dirs
+
+An additional variable that can affect FCC settings in some cases is
+`message-directory`. Emacs message-mode uses this variable for
+postponed messages.
+
+To customize both variables at the same time, use the fancy command:
M-x customize-apropos<RET>\(notmuch-fcc-dirs\)\|\(message-directory\)
interface (using the `[save]` button next to a previous search) or by
customising the variable `notmuch-saved-searches`.
-An example setting might be:
+An example setting for notmuch versions up to 0.17.x might be:
(setq notmuch-saved-searches '(("inbox" . "tag:inbox")
("unread" . "tag:inbox AND tag:unread")
("notmuch" . "tag:inbox AND to:notmuchmail.org")))
+Starting from notmuch 0.18 the variable changed. It is backwards
+compatible so the above will still work but the new style will be used
+if you use customize and there are some new features available. The above would become
+
+ (setq notmuch-saved-searches '((:name "inbox" :query "tag:inbox")
+ (:name "unread" :query "tag:inbox AND tag:unread")
+ (:name "notmuch" :query "tag:inbox AND to:notmuchmail.org")))
+
+The additional features are the possibility to set the search order
+for the search, and the possibility to specify a different query for
+displaying the count for the saved-search. For example
+
+ (setq notmuch-saved-searches '((:name "inbox"
+ :query "tag:inbox"
+ :count-query "tag:inbox and tag:unread"
+ :sort-order 'oldest-first)))
+
+specifies a single saved search for inbox, but the number displayed by
+the search will be the number of unread messages in the inbox, and the
+sort order for this search will be oldest-first.
+
Of course, you can have any number of saved searches, each configured
-with any supported search terms (see "notmuch help search-terms").
+with any supported search terms (see "notmuch help search-terms"), and
+in the new style variable they can each have different count-queries
+and sort orders.
Some users find it useful to add `and not tag:delete` to those
searches, as they use the `delete` tag to mark messages as
You can also use the notmuch database as a mail address book itself.
To do this you need a command line tool that outputs likely address
-candidates based on a search string. There are currently three
+candidates based on a search string. There are currently four
available:
* The python tool `notmuch_address.py` (`git clone
cc -o addrlookup addrlookup.c `pkg-config --cflags --libs gobject-2.0` -lnotmuch
- * Shell/fgrep/perl combination [nottoomuch-addresses.sh](http://www.iki.fi/too/nottoomuch/nottoomuch-addresses/).
+ * Shell/fgrep/perl combination [nottoomuch-addresses.sh](https://github.com/domo141/nottoomuch/blob/master/nottoomuch-addresses.rst).
This tools maintains it's own address "database" gathered from email
files notmuch knows and search from that "database" is done by `fgrep(1)`.
+ * python/sqlite combination [notmuch-abook](https://github.com/guyzmo/notmuch-abook/)
+ This tools also maintains an address database in sqlite after harvesting
+ from notmuch. It also includes a vim plugin.
+
You can perform tab-completion using any of these programs.
Just add the following to your .emacs:
You can add the sender of a message to Google Contacts by piping the message
(`notmuch-show-pipe-message`) to `goobook add`.
+### Akonadi
+
+ git clone https://github.com/mmehnert/akonadimailsearch
+
+Install the development packages for kdepim on your system.
+Enter the cloned repository and create a build directory:
+
+ mkdir build
+ cd build
+ cmake ..; make;
+
+You will find the akonadimailsearch binary in the build/src directory.
+Create a ~/bin/akonadimailsearch.sh file with the following content and make it executable:
+(Adjust the path for the akonadimailsearch binary.)
+
+ #!/bin/sh
+ akonadimailsearch "$@" 2>/dev/null
+
+As described above, you can now add the following settings to your ~/.emacs file:
+
+ (require 'notmuch-address)
+ (setq notmuch-address-command "~/bin/akonadimailsearch.sh")
+ (notmuch-address-message-insinuate)
+
+
+
## How to sign/encrypt messages with gpg
-Messages can by signed using gpg by invoking
+Messages can by signed using gpg by invoking
`M-x mml-secure-sign-pgpmime` (or `M-x mml-secure-encrypt-pgpmime`).
-These functions are available via the standard `message-mode` keybindings
+These functions are available via the standard `message-mode` keybindings
`C-c C-m s p` and `C-c C-m c p`. To sign outgoing mail by default, use the
`message-setup-hook` in your `.emacs` file:
Looking into `gnus-alias.el` gives a bit more information...
-### Example Configuration
+### Example Configuration
Here is an example configuration.
;; Use "home" identity by default
(setq gnus-alias-default-identity "home")
;; Define rules to match work identity
- (setq gnus-alias-identity-rules
+ (setq gnus-alias-identity-rules)
'(("work" ("any" "john.doe@\\(example\\.com\\|help\\.example.com\\)" both) "work"))
;; Determine identity when message-mode loads
(add-hook 'message-setup-hook 'gnus-alias-determine-identity)
M-x describe-variable RET gnus-alias-default-identity
M-x customize-group RET gnus-alias RET
- or
+ or
M-x gnus-alias-customize RET
The last two do the same thing.
lines. Add this to your `.emacs` to replace tabs with spaces in subject
lines:
- (defun notmuch-show-subject-tabs-to-spaces ()
- "Replace tabs with spaces in subject line."
- (goto-char (point-min))
- (when (re-search-forward "^Subject:" nil t)
- (while (re-search-forward "\t" (line-end-position) t)
- (replace-match " " nil nil))))
+ (defun notmuch-show-subject-tabs-to-spaces ()
+ "Replace tabs with spaces in subject line."
+ (goto-char (point-min))
+ (when (re-search-forward "^Subject:" nil t)
+ (while (re-search-forward "\t" (line-end-position) t)
+ (replace-match " " nil nil))))
- (add-hook 'notmuch-show-markup-headers-hook 'notmuch-show-subject-tabs-to-spaces)
+ (add-hook 'notmuch-show-markup-headers-hook 'notmuch-show-subject-tabs-to-spaces)
And in header lines (this will only work with the yet to be released
notmuch version 0.15):
- (defun notmuch-show-header-tabs-to-spaces ()
- "Replace tabs with spaces in header line."
- (setq header-line-format
- (notmuch-show-strip-re
- (replace-regexp-in-string "\t" " " (notmuch-show-get-subject)))))
+ (defun notmuch-show-header-tabs-to-spaces ()
+ "Replace tabs with spaces in header line."
+ (setq header-line-format
+ (notmuch-show-strip-re
+ (replace-regexp-in-string "\t" " " (notmuch-show-get-subject)))))
+
+ (add-hook 'notmuch-show-hook 'notmuch-show-header-tabs-to-spaces)
+
+## Hiding unread messages in notmuch-show
+
+I like to have an inbox saved search, but only show unread messages when they
+view a thread. This takes two steps:
+
+1. Apply
+[this patch from Mark Walters](http://notmuchmail.org/pipermail/notmuch/2012/010817.html)
+to add the `notmuch-show-filter-thread` function.
+1. Add the following hook to your emacs configuration:
+
+ (defun expand-only-unread-hook () (interactive)
+ (let ((unread nil)
+ (open (notmuch-show-get-message-ids-for-open-messages)))
+ (notmuch-show-mapc (lambda ()
+ (when (member "unread" (notmuch-show-get-tags))
+ (setq unread t))))
+ (when unread
+ (let ((notmuch-show-hook (remove 'expand-only-unread-hook notmuch-show-hook)))
+ (notmuch-show-filter-thread "tag:unread")))))
+
+ (add-hook 'notmuch-show-hook 'expand-only-unread-hook)
+
+## Changing the color of a saved search based on some other search
+
+I like to have a saved search for my inbox, but have it change color when there
+are thread with unread messages in the inbox. I accomplish this with the
+following code in my emacs config:
+
+ (defun color-inbox-if-unread () (interactive)
+ (save-excursion
+ (goto-char (point-min))
+ (let ((cnt (car (process-lines "notmuch" "count" "tag:inbox and tag:unread"))))
+ (when (> (string-to-number cnt) 0)
+ (save-excursion
+ (when (search-forward "inbox" (point-max) t)
+ (let* ((overlays (overlays-in (match-beginning 0) (match-end 0)))
+ (overlay (car overlays)))
+ (when overlay
+ (overlay-put overlay 'face '((:inherit bold) (:foreground "green")))))))))))
+ (add-hook 'notmuch-hello-refresh-hook 'color-inbox-if-unread)
+
+## Linking to notmuch messages and threads from the Circe IRC client
+
+[Circe](https://github.com/jorgenschaefer/circe/wiki) is an IRC client for emacs.
+To have clickable buttons for notmuch messages and threads, add the following to
+`lui-buttons-list` (using, e.g. M-x customize-variable)
+
+ ("\\(?:id\\|mid\\|thread\\):[0-9A-Za-z][0-9A-Za-z.@-]*" 0 notmuch-show 0)
+
+If you have notmuch-pick installed, it works fine for this as well.
+
+## Linking to notmuch messages from org-mode
+
+Support for linking to notmuch messages is distributed with org-mode,
+but as a contrib file, so you might have to work a bit to load it.
+
+In Debian and derivatives,
+
+ (add-to-list 'load-path "/usr/share/org-mode/lisp")
+
+Then
+
+ (require 'org-notmuch)
+
+In general it is nice to have a key for org-links (not just for notmuch). For example
+
+ (define-key global-map "\C-cl" 'org-store-link)
+
+## Viewing diffs in notmuch
+
+The following code allows you to view an inline patch in diff-mode
+directly from notmuch. This means that normal diff-mode commands like
+refine, next hunk etc all work.
+
+ (defun my-notmuch-show-view-as-patch ()
+ "View the the current message as a patch."
+ (interactive)
+ (let* ((id (notmuch-show-get-message-id))
+ (subject (concat "Subject: " (notmuch-show-get-subject) "\n"))
+ (diff-default-read-only t)
+ (buf (get-buffer-create (concat "*notmuch-patch-" id "*")))
+ (map (make-sparse-keymap)))
+ (define-key map "q" 'notmuch-kill-this-buffer)
+ (switch-to-buffer buf)
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (insert subject)
+ (insert (notmuch-get-bodypart-internal id 1 nil)))
+ (set-buffer-modified-p nil)
+ (diff-mode)
+ (lexical-let ((new-ro-bind (cons 'buffer-read-only map)))
+ (add-to-list 'minor-mode-overriding-map-alist new-ro-bind))
+ (goto-char (point-min))))
+
+and then this function needs to bound into the keymap with something like
- (add-hook 'notmuch-show-hook 'notmuch-show-header-tabs-to-spaces)
+ (define-key 'notmuch-show-mode-map "D" 'my-notmuch-show-view-as-patch)