X-Git-Url: https://git.cworth.org/git?p=notmuch-wiki;a=blobdiff_plain;f=emacstips.mdwn;h=58641771798bd3f61bb34bd8b6bf7613f4f8fb77;hp=c9b95b84db035d400e563cd537da29b1cec2c84d;hb=HEAD;hpb=fb8d434089150c6ffa5a71373552e386ef89c151 diff --git a/emacstips.mdwn b/emacstips.mdwn index c9b95b8..288690c 100644 --- a/emacstips.mdwn +++ b/emacstips.mdwn @@ -1,261 +1,1028 @@ [[!img notmuch-logo.png alt="Notmuch logo" class="left"]] -#Tips and Tricks for using notmuch with Emacs +# Tips and Tricks for using Notmuch with Emacs -The main Notmuch message reading client is **notmuch.el**, which is an -[emacs](http://www.gnu.org/software/emacs/) major mode, and is -included in the notmuch package. +Here are some tips and tricks for using Notmuch with Emacs. See the [[Notmuch +Emacs Interface|notmuch-emacs]] page for basics. -## Setup +[[!toc levels=2]] -To use the Notmuch emacs mode, first add the following line to your -.emacs rc file: +## Controlling external handlers for attachments - (require 'notmuch) +You can choose e.g. which pdf viewer to invoke from notmuch-show mode by +adding a .mailcap file in your home directory. Here is an example: -Then, either run "emacs -f notmuch", or execute the command "M-x -notmuch" from within a running emacs. - -## Navigating & reading mails + application/pdf; /usr/bin/mupdf %s; test=test "$DISPLAY" != ""; description=Portable Document Format; nametemplate=%s.pdf + application/x-pdf; /usr/bin/mupdf %s; test=test "$DISPLAY" != ""; description=Portable Document Format; nametemplate=%s.pdf -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. +### Convert ".pdf" and ".docx" to text and pop to buffer -Executing a search will open a new buffer in notmuch-search-mode -displaying the search results. Each line in the search results -represents a message thread. Hitting the '?' key will show help for -this mode. +Add the following (hacky but effective!) code to `.emacs.d/notmuch-config.el`; +the overwritten `defcustom` will change action when pressing RET on top of an +attachment; ".pdf" and ".docx" attachments are converted to text (using +"pdf2text" and "docx2txt.pl" commands to do the conversion), saving to file +(the default action of `notmuch-show-part-button-default-action`) is offered +to attachments of other types. -In general, the 'q' will kill the current notmuch buffer and return -you to the previous buffer (sort of like a 'pop'). + (defun user/mm-pipe-- (handle cmd) + ;; conveniently, '-' '-' a args to pdftotext and docx2txt.pl work fine + ;; fixme: naming inconsistency (fn name and buffer name) + (let ((buffer (get-buffer-create "*attachment-to-text*"))) + (with-current-buffer buffer + (setq buffer-read-only nil) + (erase-buffer)) + (with-temp-buffer + ;; "based on mm-pipe-part in mm-decode.el" + (mm-with-unibyte-buffer + (mm-insert-part handle) + (mm-add-meta-html-tag handle) + (let ((coding-system-for-write 'binary)) + (call-process-region (point-min) (point-max) + cmd nil buffer nil "-" "-")))) + (pop-to-buffer buffer) + (goto-char (point-min)) + (text-mode) + (visual-line-mode) + (view-mode))) -In search mode, navigating to a thread and hitting return will then -open a new buffer in notmuch-show-mode, which will show the actual -message contents of the thread. + (defun user/notmuch-show-pop-attachment-to-buffer () + ;; "based on notmuch-show-apply-to-current-part-handle" + (interactive) + (let ((handle (notmuch-show-current-part-handle))) + ;;(message "%s" handle) + (unwind-protect + (pcase (car (nth 1 handle)) + ("application/pdf" + (user/mm-pipe-- handle "pdftotext")) + ("application/vnd.openxmlformats-officedocument.wordprocessingml.document" + (user/mm-pipe-- handle "docx2txt.pl")) + (_ (notmuch-show-save-part))) + (kill-buffer (mm-handle-buffer handle))))) -## Sending mail + (setq notmuch-show-part-button-default-action + #'user/notmuch-show-pop-attachment-to-buffer) -In any notmuch mode, you can start a new message by hitting the 'm' -key. To reply to a message or thread, just hit the 'r' key. +## Overwriting the sender address -When composing new messages, you will be entered in emacs's -"message-mode", which is a powerful mode for composing and sending -messages. When in message move, you can type "C-c ?" for help. +If you want to always use the same sender address, then the following +defadvice can help you. -If you would like to use address autocompletion, see the [address -completion howto](#address_completion)" below. + (defadvice notmuch-mua-reply (around notmuch-fix-sender) + (let ((sender "Max Monster ")) + ad-do-it)) + (ad-activate 'notmuch-mua-reply) -When you are ready to send a message, type "C-c C-c". By default -message mode will use your sendmail command to send mail, so make sure -that works. One annoying standard configuration of message mode is -that it will hide the sent mail in your emacs frame stack, but it will -not close it. If you type several mails in an emacs session they will -accumulate and make switching between buffers more annoying. You can -avoid that behavior by adding `(setq message-kill-buffer-on-exit t)` -in your .emacs file which will really close the mail window after -sending it. +## Initial cursor position in notmuch 0.15 hello window -## Attaching files +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. -Using the `M-x mml-attach-file` command, you can attach any file to be -send with your mail. By default this command is bound to the menu item -`Attachments--Attach File` with the key binding `C-c C-a`. The -variable `mml-dnd-attach-options` (M-x -customize-variable`mml-dnd-attach-options`) can be set to allow -the prompting for various attachment options (such as -inline/attachement) if you want to do that. - -For those who prefer graphics, you can also simply drag and drop files -from a file manager into a mail composition window to have it attached -(assuming you are using emacs with X support). At least in Ubuntu it -works by dragging from the file manager without any modifications. - ------ - -

Advanced tips and tweaks

- -* **Add a keybinding to add/remove/toggle a tag.** - - Here's an example of how to add a key binding to notmuch-show-mode - to toggle a "deleted" tag: - - (define-key notmuch-show-mode-map "d" - (lambda () - "toggle deleted tag for message" - (interactive) - (if (member "deleted" (notmuch-show-get-tags)) - (notmuch-show-remove-tag "deleted") - (notmuch-show-add-tag "deleted")))) - - You can do the same for threads in notmuch-search-mode by just - replacing "show" with "search" in the called functions. This - definition makes use of a lambda function, but you could just as - easily defined a separate function first: - - (defun notmuch-show-toggle-deleted-tag() - "toggle deleted tag for message" - (interactive) - (if (member "deleted" (notmuch-show-get-tags)) - (notmuch-show-remove-tag "deleted") - (notmuch-show-add-tag "deleted"))) - (define-key notmuch-show-mode-map "d" 'notmuch-show-toggle-deleted-tag) - -* **How to do FCC/BCC...** - - Any notmuch reply will automatically include your primary email - address in a BCC so that any messages you send will (eventually) end - up in your mail store as well. But this doesn't do anything for - messages that you compose that are not replies. - - Another method is to save the file in a folder of your local - Maildir, usually called FCC (file carbon copy). You can achieve this - 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: - - M-x customize-apropos\(notmuch-fcc-dirs\)\|\(message-directory\) - - This method will even allow you to select different outboxes - depending on your selected from address, if you need that - functionality. Please see the documentation on the variable in the - customization window for how to do so. - -* **How to customize notmuch-folders** - - There's a "notmuch-folder" command available in the emacs client - that displays a list of "folders" and the number of messages in - each. Each folder is simply a named search specification. To - configure this mode, edit your ${HOME}/.emacs file and include text - something like the following: - - (setq notmuch-folders '(("inbox" . "tag:inbox") - ("unread" . "tag:inbox AND tag:unread") - ("notmuch" . "tag:inbox AND to:notmuchmail.org"))) - - Of course, you can have any number of folders, each configured - with any supported search terms (see "notmuch help search-terms"). - - Personally, I find it fairly useful to add "not tag:delete" to those - views as I use that tag to mark messages as deleted and it - automatically removes them from my standard views. Use whatever - seems most useful to you. - -* **Viewing HTML messages with an external viewer** - - The emacs client can often display an HTML message inline, but it - sometimes fails for one reason or another, (or is perhaps inadequate - if you really need to see the graphical presentation of the HTML - message). - - In this case, it can be useful to display the message in an external - viewer, such as a web browser. Here's a little script that Keith - Packard wrote, which he calls view-html: - - #!/bin/sh - dir=3D`mktemp -d` - trap "rm -r $dir" 0 - cat "$@" > "$dir"/msg - if munpack -C "$dir" -t < "$dir"/msg 2>&1 | grep 'Did not find'; then - sed -n '/[Hh][Tt][Mm][Ll]/,$p' "$dir"/msg > $dir/part1.html - rm "$dir"/msg - fi - for i in "$dir"/part*; do - if grep -q -i -e '' -e 'text/html' "$i"; then - iceweasel "$i" & - sleep 3 - exit 0 - fi - done - - Save that script somewhere in your ${PATH}, make it executable, and - change the invocation of iceweasel to any other HTML viewer if - necessary. Then within the emacs client, press "|" to pipe the - current message, then type "view-html". - - Keith mentions the following caveat, "Note that if iceweasel isn't - already running, it seems to shut down when the script exits. I - don't know why." - -* **msmtp, message mode and multiple accounts** - - As an alternative to running a mail server such as sendmail or - postfix just to send email, it is possible to use - [msmtp](http://msmtp.sourceforge.net/). This small application will - look like /usr/bin/sendmail to a MUA such as emacs message mode, but - will just forward the email to an external SMTP server. It's fairly - easy to set up and it support several account for using different - SMTP servers. The msmtp pages have several examples. - - A typical scenario is that you want to use the company SMTP server - for email coming from your company email address, and your personal - server for personal email. If msmtp is passed the envelope address - on the command line (the -f/--from option) it will automatically - pick the matching account. The only trick here seems to be getting - emacs to actually pass the envelope from. There are a number of - overlapping configuration variables that control this, and it's a - little confusion, but setting these three works for me: - - - mail-specify-envelope-from: t - - - message-sendmail-envelope-from: header - - - mail-envelope-from: header - - With that in place, you need a .msmtprc with the accounts configured - for the domains you want to send out using specific SMTP servers and - the rest will go to the default account. - -* **how to get email address completion** - - There are currently two solutions to this: - - * [bbdb](http://bbdb.sourceforge.net) is a contact database for - emacs that works quite nicely together with message mode, - including address autocompletion. - - * 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 two available: - - * The python tool notmuch_address.py ('git clone - http://jkr.acm.jhu.edu/git/notmuch_addresses.git`) (slower, but - no compilation required so good for testing the setup) - - * The vala-based - [addrlookup](http://github.com/spaetz/vala-notmuch) (faster, but - needs compiling). This is how you compile the (3rd party) tool - "addrlookup" to give you address completion: - - * you need the addrlookup binary, first of all. Grab - http://github.com/spaetz/vala-notmuch/raw/static-sources/src/addrlookup.c - and build it with `cc -o addrlookup addrlookup.c ``pkg-config - --cflags --libs gobject-2.0`` -lnotmuch`. That should give you - the binary that you can test already. - - * EUDC is integrated into emacs and can be used for tab - completion of email addresses. The code I use is here - http://gist.github.com/359425. It was announce in [this - mail](http://mid.gmane.org/87fx3uflkx.fsf@jhu.edu) - (id:87fx3uflkx.fsf@jhu.edu) which contains links to the git - repositories which contain the files. - -* **how to sign/encrypt my messages with - gpg** - - You can manually sign your messages with gpg by invoking `M-x - mml-secure-sign-pgpmime` (or `M-x - mml-secure-encrypt-pgpmime`). These functions are available via the - convenient (*cough cough*) keybindings `C-c C-m s p` and `C-c C-m c - p` by default. To sign my outgoing mail by default, I use this hook - in my .emacs file: - - ;;sign messages by default - (add-hook 'message-setup-hook 'mml-secure-sign-pgpmime) - - This inserts the blurb `<#part sign=pgpmime>` into the beginning of - my mail text body and will be converted into a pgp signature when - sending (so I can just manually delete that line if I do not want a - mail to be signed). +Initially the cursor is positioned at the beginning of buffer. + +Some users liked the "ancient" version where cursor was moved to the +first `Saved searches` button. + +Add the following code to your notmuch emacs configuration file in +case you want this behaviour: + + (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))))) + +## Add a key binding to add/remove/toggle a tag + +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. + +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-show-mode-map "S" + (lambda () + "mark message as spam" + (interactive) + (notmuch-show-tag (list "+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`. + +You may also want the function in search mode apply to the all threads +in the selected region (if there is one). For notmuch prior to 0.17 +this behaviour will occur automatically with the functions given +above. To get this behaviour on 0.17+ do the following: + + (define-key notmuch-search-mode-map "S" + (lambda (&optional beg end) + "mark thread as spam" + (interactive (notmuch-interactive-region)) + (notmuch-search-tag (list "+spam" "-inbox") beg end))) + +The analogous functionality in notmuch-tree is currently missing. + +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 (list "+spam" "-inbox"))) + + (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: + + (define-key notmuch-show-mode-map "d" + (lambda () + "toggle deleted tag for message" + (interactive) + (if (member "deleted" (notmuch-show-get-tags)) + (notmuch-show-tag (list "-deleted")) + (notmuch-show-tag (list "+deleted"))))) + +## Adding many tagging keybindings + +If you want to have have many tagging keybindings, you can save the typing +the few lines of boilerplate for every binding (for versions before 0.12, +you will need to change notmuch-show-apply-tag-macro). + + (eval-after-load 'notmuch-show + '(define-key notmuch-show-mode-map "`" 'notmuch-show-apply-tag-macro)) + + (setq notmuch-show-tag-macro-alist + (list + '("m" "+notmuch::patch" "+notmuch::moreinfo" "-notmuch::needs-review") + '("n" "+notmuch::patch" "+notmuch::needs-review" "-notmuch::pushed") + '("o" "+notmuch::patch" "+notmuch::obsolete" + "-notmuch::needs-review" "-notmuch::moreinfo") + '("p" "-notmuch::pushed" "-notmuch::needs-review" + "-notmuch::moreinfo" "+pending") + '("P" "-pending" "-notmuch::needs-review" "-notmuch::moreinfo" "+notmuch::pushed") + '("r" "-notmuch::patch" "+notmuch::review") + '("s" "+notmuch::patch" "-notmuch::obsolete" "-notmuch::needs-review" "-notmuch::moreinfo" "+notmuch::stale") + '("t" "+notmuch::patch" "-notmuch::needs-review" "+notmuch::trivial") + '("w" "+notmuch::patch" "+notmuch::wip" "-notmuch::needs-review"))) + + (defun notmuch-show-apply-tag-macro (key) + (interactive "k") + (let ((macro (assoc key notmuch-show-tag-macro-alist))) + (apply 'notmuch-show-tag-message (cdr macro)))) + +## Restore reply-to-all key binding to 'r' + +Starting from notmuch 0.12 the 'r' key is bound to reply-to-sender instead of +reply-to-all. Here's how to swap the reply to sender/all bindings in show mode: + + (define-key notmuch-show-mode-map "r" 'notmuch-show-reply) + (define-key notmuch-show-mode-map "R" 'notmuch-show-reply-sender) + +In search mode: + + (define-key notmuch-search-mode-map "r" 'notmuch-search-reply-to-thread) + (define-key notmuch-search-mode-map "R" 'notmuch-search-reply-to-thread-sender) + +And in tree mode: + + (define-key notmuch-tree-mode-map "r" (notmuch-tree-close-message-pane-and #'notmuch-show-reply)) + (define-key notmuch-tree-mode-map "R" (notmuch-tree-close-message-pane-and #'notmuch-show-reply-sender)) + +## How to do FCC/BCC... + +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 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\(notmuch-fcc-dirs\)\|\(message-directory\) + +This mechanism also allows you to select different folders to be +used for the outgoing mail depending on your selected `From` +address. Please see the documentation for the variable +`notmuch-fcc-dirs` in the customization window for how to arrange +this. + +The `notmuch-fcc-dirs` variable is only taken into account when mails +are composed using `notmuch-mua-mail`. If you want to use the notmuch +mail user agent by default, the `mail-user-agent` needs to be +customized to use the `notmuch-user-agent`. + +## How to customize `notmuch-saved-searches` + +When starting notmuch, a list of saved searches and message counts is +displayed, replacing the older `notmuch-folders` command. The set of +saved searches displayed can be modified directly from the notmuch +interface (using the `[save]` button next to a previous search) or by +customising the variable `notmuch-saved-searches`. + +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"), 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 +deleted. This causes messages that are marked as deleted to be removed +from the commonly used views of messages. Use whatever seems most +useful to you. + +## Viewing HTML messages with an external viewer + +The Emacs client can generally display HTML messages inline using one of the +supported HTML renderers. This is controlled by the `mm-text-html-renderer` +variable. + +Sometimes it may be necessary to display the message, or a single MIME part, in +an external browser. This can be done by `(notmuch-show-view-part)`, bound to +`. v` by default. + +This command will try to view the message part the point is on with an +external viewer. The mime-type of the part will determine what viewer +will be used. Typically a 'text/html' part will be send to your +browser. + +The configuration for this is kept in so called `mailcap` +files. (typically the file is `~/.mailcap` or `/etc/mailcap`) If the +wrong viewer is started or something else goes wrong, there's a good +chance something needs to be adapted in the mailcap configuration. + +For Example: The `copiousoutput` setting in mailcap files needs to be +removed for some mime-types to prevent immediate removal of temporary +files so the configured viewer can access them. + + +## msmtp, message mode and multiple accounts + +As an alternative to running a mail server such as sendmail or postfix +just to send email, it is possible to use +[msmtp](http://msmtp.sourceforge.net/). This small application will +look like `/usr/bin/sendmail` to a MUA such as emacs message mode, but +will just forward the email to an external SMTP server. It's fairly +easy to set up and it supports several accounts for using different +SMTP servers. The msmtp pages have several examples. + +A typical scenario is that you want to use the company SMTP server +for email coming from your company email address, and your personal +server for personal email. If msmtp is passed the envelope address +on the command line (the -f/--from option) it will automatically +pick the matching account. The only trick here seems to be getting +emacs to actually pass the envelope from. There are a number of +overlapping configuration variables that control this, and it's a +little confusion, but setting these three works for me: + + - `mail-specify-envelope-from`: `t` + + - `message-sendmail-envelope-from`: `header` + + - `mail-envelope-from`: `header` + +With that in place, you need a `.msmtprc` with the accounts configured +for the domains you want to send out using specific SMTP servers and +the rest will go to the default account. + +## sending mail using smtpmail + + + +If setting up local `sendmail` or `msmtp` is not feasible or desirable, +the Emacs `smtpmail` package can be used to send email by talking to remote +SMTP server via TCP connection. It is pretty easy to configure: + +1. Emacs variable `message-send-mail-function` has not been set + + Initially, Emacs variable `message-send-mail-function` has value of + `sendmail-query-once`. When (notmuch) message mode is about to send email, + `sendmail-query-once` will ask how emacs should send email. Typing `smtp` + will configure `smtpmail` and Emacs may prompt for SMTP settings. + +1. `M-x customize-group RET smtpmail` + + As a minimum, 'Smtpmail Smtp Server' needs to be set. + + After doing that, continue with `M-x load-library RET message` and + `M-x customize-variable RET message-send-mail-function`. + In the customization buffer select `message-smtpmail-send-it`. + +1. Set some variables in .emacs or in [notmuch init file](/notmuch-emacs#notmuch_init_file) + + (setq smtpmail-smtp-server "smtp.server.tld" ;; <-- edit this !!! + ;; smtpmail-smtp-service 25 ;; 25 is default -- uncomment and edit if needed + ;; smtpmail-stream-type 'starttls + ;; smtpmail-debug-info t + ;; smtpmail-debug-verb t + message-send-mail-function 'message-smtpmail-send-it) + +Note that emacs 24 or newer is required for `smtpmail-stream-type` +(and smtp authentication) to be effective. + +More information for smtpmail is available: + +* In Emacs: `M-x info-display-manual smtpmail` +* [EmacsWiki Page](http://www.emacswiki.org/emacs/SendingMail) + + +## Address completion when composing + +There are currently three solutions to this: + +### notmuch address + +Starting with Notmuch 0.21, there is a builtin command to perform +autocompletion directly within Notmuch. Starting with 0.22, it is +configured by default, so if you have previously configured another +completion mechanism, you may want to try out the new internal +method. Use `M-x customize-variable RET notmuch-address-command` and +reset the value to "internal address completion" (`'internal` in +lisp). + +If you are not yet running 0.22, you can still use it by adding a +wrapper around the command called, say, `notmuch-address`: + + #!/bin/sh + exec notmuch address from:"$*" + +Then you can set the `notmuch-address-command` to `notmuch-address` +(if it is in your `$PATH` of course, otherwise use an absolute path). + +### bbdb + +[bbdb](http://bbdb.sourceforge.net) is a contact database for emacs +that works quite nicely together with message mode, including +address autocompletion. + +### notmuch database as an address book + +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 four +available: + + * The python tool `notmuch_address.py` (`git clone + http://commonmeasure.org/~jkr/git/notmuch_addresses.git`) (slower, but + no compilation required so good for testing the setup) + + * The C-based [notmuch-addrlookup](https://github.com/aperezdc/notmuch-addrlookup-c) by [Adrian Perez](http://perezdecastro.org/), which is faster but needs to be compiled. + + git clone https://github.com/aperezdc/notmuch-addrlookup-c + cd notmuch-addrlookup-c + make + + * The vala-based + [addrlookup](http://github.com/spaetz/vala-notmuch) The addrlookup binary needs to be compiled. + Grab + `http://github.com/spaetz/vala-notmuch/raw/static-sources/src/addrlookup.c` + and build it with: + + cc -o addrlookup addrlookup.c `pkg-config --cflags --libs gobject-2.0` -lnotmuch + + * Shell/fgrep/perl combination [nottoomuch-addresses.sh](https://github.com/domo141/nottoomuch/blob/master/nottoomuch-addresses.rst). + This tools maintains its 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 [notmuch init file](/notmuch-emacs#notmuch_init_file): + + (require 'notmuch-address) + (setq notmuch-address-command "/path/to/address_fetching_program") + (notmuch-address-message-insinuate) + +### Google Contacts + +[GooBook](http://code.google.com/p/goobook/) is a command-line tool for +accessing Google Contacts. Install and set it up according to its documentation. + +To use GooBook with notmuch, use this wrapper script and set it up like the +programs above. + + #!/bin/sh + goobook query "$*" | sed 's/\(.*\)\t\(.*\)\t.*/\2 \<\1\>/' | sed '/^$/d' + +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. Copy it to ~/bin . + +You can now add the following settings to your +[notmuch init file](/notmuch-emacs#notmuch_init_file): + + (require 'notmuch-address) + (setq notmuch-address-command "~/bin/akonadimailsearch") + (notmuch-address-message-insinuate) + +### Completion selection with helm + +An address query might return multiple possible matches from which you +will have to select one. To ease this task, several different +frameworks in emacs support completion selection. One of them is +[helm](https://github.com/emacs-helm/helm). The following snippet +improves the out-of-the-box support for helm in notmuch as it enables +the required-match option and also does not ignore the first returned +address. + + (setq notmuch-address-selection-function + (lambda (prompt collection initial-input) + (completing-read prompt (cons initial-input collection) nil t nil 'notmuch-address-history))) + + +## How to sign/encrypt messages with gpg + +Messages can be 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 +`C-c C-m s p` and `C-c C-m c p`. + +In Emacs 28 you will be asked whether to sign the message using the +sender and are offered to remember your choice. In Emacs 27 you will +get a slightly misleading error and have to manually add the following +line to you init file. Older Emacsen just do this unconditionally. + + (setq mml-secure-openpgp-sign-with-sender t) + +To sign outgoing mail by default, use the `message-setup-hook` in your +init file: + + ;; Sign messages by default. + (add-hook 'message-setup-hook 'mml-secure-sign-pgpmime) + +This inserts the required `<#part sign=pgpmime>` into the beginning +of the mail text body and will be converted into a pgp signature +when sending (so one can just manually delete that line if signing +is not required). + +Alternatively, you may prefer to use `mml-secure-message-sign-pgpmime` instead +of `mml-secure-sign-pgpmime` to sign the whole message instead of just one +part. + +If you want to automatically encrypt outgoing messages if the keyring +contains a public key for every recipient, you can add something like +that to your `.emacs` file: + + (defun message-recipients () + "Return a list of all recipients in the message, looking at TO, CC and BCC. + + Each recipient is in the format of `mail-extract-address-components'." + (mapcan (lambda (header) + (let ((header-value (message-fetch-field header))) + (and + header-value + (mail-extract-address-components header-value t)))) + '("To" "Cc" "Bcc"))) + + (defun message-all-epg-keys-available-p () + "Return non-nil if the pgp keyring has a public key for each recipient." + (require 'epa) + (let ((context (epg-make-context epa-protocol))) + (catch 'break + (dolist (recipient (message-recipients)) + (let ((recipient-email (cadr recipient))) + (when (and recipient-email (not (epg-list-keys context recipient-email))) + (throw 'break nil)))) + t))) + + (defun message-sign-encrypt-if-all-keys-available () + "Add MML tag to encrypt message when there is a key for each recipient. + + Consider adding this function to `message-send-hook' to + systematically send encrypted emails when possible." + (when (message-all-epg-keys-available-p) + (mml-secure-message-sign-encrypt))) + + (add-hook 'message-send-hook #'message-sign-encrypt-if-all-keys-available + +### Troubleshooting message-mode gpg support + +- If you have trouble with expired subkeys, you may have encountered + emacs bug #7931. This is fixed in git commit 301ea744c on + 2011-02-02. Note that if you have the Debian package easypg + installed, it will shadow the fixed version of easypg included with + emacs. + +- If you wish `mml-secure-encrypt` to encrypt also for the sender, then + `M-x customize-variable mml2015-encrypt-to-self` might suit your need. + +## Reading and verifying encrypted and signed messages + +Encrypted and signed mime messages can be read and verified with: + + (setq notmuch-crypto-process-mime t) + +Decrypting inline pgp messages can be done by selecting an the inline pgp area +and using: + + M-x epa-decrypt-region RET + +Verifying of inline pgp messages is not supported directly ([reasons +here](https://dkg.fifthhorseman.net/notes/inline-pgp-harmful/)). You can still +verify a part using + + M-x notmuch-show-pipe-part RET gpg --verify RET + +## Multiple identities using gnus-alias + +[gnus-alias](http://www.emacswiki.org/emacs/GnusAlias) allows you to +define multiple identities when using `message-mode`. You can specify +the from address, organization, extra headers (including *Bcc*), extra +body text, and signature for each identity. Identities are chosen +based on a set of rules. When you are in message mode, you can switch +identities using gnus-alias. + +### Installation + +- put `gnus-alias.el` on your load Emacs-Lisp load path (add new directory + to load path by writing `(add-to-list 'load-path "/some/load/path")` into + your `.emacs`. + +- Add the following to your `.emacs` + + (autoload 'gnus-alias-determine-identity "gnus-alias" "" t) + (add-hook 'message-setup-hook 'gnus-alias-determine-identity) + +Looking into `gnus-alias.el` gives a bit more information... + +### Example Configuration + +Here is an example configuration. + + ;; Define two identities, "home" and "work" + (setq gnus-alias-identity-alist + '(("home" + nil ;; Does not refer to any other identity + "John Doe " ;; Sender address + nil ;; No organization header + nil ;; No extra headers + nil ;; No extra body text + "~/.signature") + ("work" + nil + "John Doe " + "Example Corp." + (("Bcc" . "john.doe@example.com")) + nil + "~/.signature.work"))) + ;; Use "home" identity by default + (setq gnus-alias-default-identity "home") + ;; Define rules to match work identity + (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) + +When `gnus-alias` has been loaded (using autoload, require, *M-x load-library* +or *M-x load-file* (load-file takes file path -- therefore it can be used +without any `.emacs` changes)) the following commands can be used to get(/set) +more information (some of these have "extensive documentation"): + + M-x describe-variable RET gnus-alias-identity-alist + M-x describe-variable RET gnus-alias-identity-rules + M-x describe-variable RET gnus-alias-default-identity + + M-x customize-group RET gnus-alias RET + or + M-x gnus-alias-customize RET + +The last two do the same thing. + +See also the **Usage:** section in `gnus-alias.el`. + +## Multiple identities (and more) with message-templ + +Another option for multiple identities is +[message-templ](http://git.tethera.net/message-templ.git) +(also a available in marmalade). This provides roughly the same +facilities as wanderlust's template facility. + +See +[example.emacs.el](https://git.tethera.net/message-templ.git/tree/example.emacs.el) +for some simple examples of usage. + +## Resending (or bouncing) messages + +Add the following to your [notmuch init file](/notmuch-emacs#notmuch_init_file) to be able +to resend the current message in show mode. + + (define-key notmuch-show-mode-map "b" + (lambda (&optional address) + "Bounce the current message." + (interactive "sBounce To: ") + (notmuch-show-view-raw-message) + (message-resend address))) + +## `notmuch-hello` refresh status message + +Add the following to your [notmuch init file](/notmuch-emacs#notmuch_init_file) to get a +status message about the change in the number of messages in the mail store +when refreshing the `notmuch-hello` buffer. + + (defvar notmuch-hello-refresh-count 0) + + (defun notmuch-hello-refresh-status-message () + (unless no-display + (let* ((new-count + (string-to-number + (car (process-lines notmuch-command "count")))) + (diff-count (- new-count notmuch-hello-refresh-count))) + (cond + ((= notmuch-hello-refresh-count 0) + (message "You have %s messages." + (notmuch-hello-nice-number new-count))) + ((> diff-count 0) + (message "You have %s more messages since last refresh." + (notmuch-hello-nice-number diff-count))) + ((< diff-count 0) + (message "You have %s fewer messages since last refresh." + (notmuch-hello-nice-number (- diff-count))))) + (setq notmuch-hello-refresh-count new-count)))) + + (add-hook 'notmuch-hello-refresh-hook 'notmuch-hello-refresh-status-message) + +## Replacing tabs with spaces in subject and header + +Mailman mailing list software rewrites and rewraps long message subjects in +a way that causes TABs to appear in the middle of the subject and header +lines. Add this to your [notmuch init file](/notmuch-emacs#notmuch_init_file) 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)))) + + (add-hook 'notmuch-show-markup-headers-hook 'notmuch-show-subject-tabs-to-spaces) + +And in header lines: + + (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](https://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") + +In NixOS, using `emacsWithPackages (epkgs: [ epkgs.orgPackages.org-plus-contrib ])`, + + (loop for p in load-path + do (if (file-accessible-directory-p p) + (let ((m (directory-files-recursively p "^ol-notmuch.el$"))) + (if m (add-to-list 'load-path (file-name-directory (car m))))))) + +Then + + (require 'ol-notmuch) + +In general it is nice to have a key for org-links (not just for notmuch). For example + + (define-key global-map "\C-c l" 'org-store-link) + +If you're using `use-package` the package can be loaded using the following: + +```emacs-lisp +(use-package ol-notmuch + :ensure t + :bind + ("C-c l" . org-store-link)) +``` + +Note the package was renamed from `org-notmuch` to `ol-notmuch` in recent +versions of org-mode. If you're using an old version of notmuch you might want +to `(require 'org-notmuch)` instead. + +## 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)) + (msg (notmuch-show-get-message-properties)) + (part (notmuch-show-get-part-properties)) + (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-bury-or-kill-this-buffer) + (switch-to-buffer buf) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert subject) + (insert (notmuch-get-bodypart-text msg part 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 to `. d` in the keymap + + (define-key 'notmuch-show-part-map "d" 'my-notmuch-show-view-as-patch) + +## Interfacing with Patchwork + +[Patchwork](http://jk.ozlabs.org/projects/patchwork/) is a web-based system for +tracking patches sent to a mailing list. While the Notmuch project doesn't use +it, many other open source projects do. Having an easy way to get from a patch +email in your favorite mail client to the web page of the patch in the Patchwork +instance is a cool thing to have. Here's how to abuse the notmuch stash feature +to achieve this. (Don't know stash? See `notmuch-show-stash-mlarchive-link`, +bound to `c l` in `notmuch-show`.) + +The trick needed is turning the email Message-ID into a unique Patchwork ID +assigned by Patchwork. We'll use the `pwclient` command-line tool to achieve +this. You'll first need to get that working and configured for the Patchwork +instance you're using. That part is beyond this tip here; please refer to +Patchwork documentation. + +Check your configuration on the command-line, for example: + + /path/to/pwclient -p -n 5 -f "%{id}" + +Note that the -f format argument may require a reasonably new version of the +client. Once you have the above working, you can `M-x customize-variable RET +notmuch-show-stash-mlarchive-link-alist RET`. + +Add a new entry with "Function returning the URL:" set to: + + (lambda (message-id) + (concat "http://patchwork.example.com/patch/" + (nth 0 + (process-lines "/path/to/pwclient" "search" + "-p" "the-project" + "-m" (concat "<" message-id ">") + "-n" "1" + "-f" "%{id}")))) + +Replacing `http://patchwork.example.com/patch/`, `/path/to/pwclient`, and +`the-project` appropriately. You should now be able to stash the Patchwork URL +using `c l`. + +Going further, if the patch has been committed, you can get the commit hash with +this: + + (lambda (message-id) + (nth 0 + (process-lines "/path/to/pwclient" "search" + "-p" "the-project" + "-m" (concat "<" message-id ">") + "-n" "1" + "-f" "%{commit_ref}"))) + +And finally, if the project has a web interface to its source repository, you +can turn the commit hash into a URL pointing there, for example: + + (lambda (message-id) + (concat "http://cgit.example.com/the-project/commit/?id=" + (nth 0 + (process-lines "/path/to/pwclient" "search" + "-p" "the-project" + "-m" (concat "<" message-id ">") + "-n" "1" + "-f" "%{commit_ref}")))) + +## Never forget attachments + +Very often we forget to actually attach the file when we send an email +that's supposed to have an attachment. Did this never happen to you? +If not, then it will. + +Since version 0.29 Notmuch includes the `notmuch-mua-attachment-check` +function. This function checks whether a message references an +attachment and if it finds none it asks for confirmation before +sending the message. The function is meant to be added to the +`message-send-hook`, like so: + + (add-hook 'message-send-hook 'notmuch-mua-attachment-check) + +The "customize"able variable `notmuch-mua-attachment-regexp` controls +how reference to an attachment are identified. By default, it +identifies English and French terms. For example, the following makes +it recognise English and Portuguese terms: + + (setq-default notmuch-mua-attachment-regexp + "\\b\\(attach\\|attachment\\|attached\\|anexo\\|anexado\\)\\b") + + +## Avoid forgetting the subject + +It happens that we forget to enter the Subject line, particularly when +we leave that to the end. It's easy to write a function that checks +whether the Subject is empty, and add it to `message-send-hook` to get +confirmation: + + (defun my-notmuch-mua-empty-subject-check () + "Request confirmation before sending a message with empty subject" + (when (and (null (message-field-value "Subject")) + (not (y-or-n-p "Subject is empty, send anyway? "))) + (error "Sending message cancelled: empty subject."))) + (add-hook 'message-send-hook 'my-notmuch-mua-empty-subject-check) + + +## Applying patches to git repositories + +The `notmuch-extract-thread-patches` and +`notmuch-extract-message-patches` commands from the `elpa-mailscripts` +package in Debian (and its derivatives) can do this for you. + +## Allow content preference based on message context + +The preference for which sub-part of a multipart/alternative part is shown is +globally set. For example, if you prefer showing the html version over the text +based, you can set: + + (setq notmuch-multipart/alternative-discouraged '("text/plain" "text/html")) + +However, sometimes you might want to adapt your preference depending on the +context. You can override the default settings on a per-message basis by +providing a function that has access to the message and which returns the +discouraged type list. For example: + + (defun my/determine-discouraged (msg) + (let* ((headers (plist-get msg :headers)) + (from (or (plist-get headers :From) ""))) + (cond + ((string-match "whatever@mail.address.com" from) + '("text/plain")) + (t + '("text/html" "multipart/related"))))) + + (setq notmuch-multipart/alternative-discouraged + 'my/determine-discouraged) + +This would discourage text/html and multipart/related generally, but discourage +text/plain should the message be sent from whatever@mail.address.com. + +## See the recipient address instead of your address when listing sent messages + +If you like to see your sent messages in unthreaded view, by default you will +see your address in the authors column, which is maybe not what you want. The +following code allows for showing the recipients if your email address (an +arbitrary address, whatever@mail.address.com in the example) is included in the +From field. + + (defun my/notmuch-unthreaded-show-recipient-if-sent (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 "whatever@mail.address.com" author) + (concat "↦ " (notmuch-tree-clean-address to)) + (notmuch-tree-clean-address to) + author)) + 'face face))) + + (setq notmuch-unthreaded-result-format + '(("date" . "%12s ") + (my/notmuch-unthreaded-show-recipient-if-sent . "%-20.20s") + ((("subject" . "%s")) + . " %-54s ") + ("tags" . "(%s)"))) + +## Issues with Emacs 24 (unsupported since notmuch 0.31 (2020-09-05)) + +If notmuch-show-mode behaves badly for you in emacs 24.x try adding one of + + (setq gnus-inhibit-images nil) + +or + + (require 'gnus-art) + +to your .emacs file.