X-Git-Url: https://git.cworth.org/git?a=blobdiff_plain;f=emacstips.mdwn;h=3a1a3dde0b3513b542dd6c071b91044d67c995ca;hb=ac053aadb3be86f206612b19868eaa08350bb8dd;hp=3eef27ebcb14ae1f097f632ec6e9003c10e5e174;hpb=40a70ec14d02a607758a34ea9cccaef347508f2a;p=notmuch-wiki diff --git a/emacstips.mdwn b/emacstips.mdwn index 3eef27e..3a1a3dd 100644 --- a/emacstips.mdwn +++ b/emacstips.mdwn @@ -1,16 +1,20 @@ - - [[!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 -[[!toc levels=2]] +One of the more popular notmuch message reading clients is +**notmuch.el**, an [emacs](http://www.gnu.org/software/emacs/) major +mode for interacting with notmuch. It is included in the notmuch +package (notmuch-emacs in Debian). This page goes over some usage +tips 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. +[[!toc levels=2]] ## Setup +Have a look at the [Howto](http://notmuchmail.org/howto/) for +prerequisites. Be sure you have done the general setup using the +notmuch cli command! + To use the Notmuch emacs mode, first add the following line to your `.emacs` rc file: @@ -26,8 +30,11 @@ notmuch` from within a running emacs. ## 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 @@ -87,12 +94,23 @@ mentioned as script arguments. (Note: The script expects that you have #!/bin/sh attach_cmds="" while [ "$1" ]; do - fullpath=$(readlink --canonicalize $1) + fullpath=$(readlink --canonicalize "$1") attach_cmds="$attach_cmds (mml-attach-file \"$fullpath\")" shift done emacsclient -a '' -c -e "(progn (compose-mail) $attach_cmds)" +## Issues with Emacs 24 + +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. ----- @@ -105,32 +123,54 @@ is an option to collect those to a separate file (which is only loaded when `notmuch` is invoked). To do this, write, for example a file called `~/.emacs.d/my-notmuch.el`: - ;; my-notmuch.el -- my notmuch mail configuration - ;; - - ;; add here stuff required to be configured *before* - ;; notmuch is loaded; + ;;; my-notmuch.el -- my notmuch mail configuration + ;;; - ; uncomment and modify in case some elisp files are not found in load-path - ; (add-to-list 'load-path "~/vc/ext/notmuch/emacs") + ;;; add here stuff required to be configured *before* + ;;; notmuch is loaded; - ;; load notmuch - (require 'notmuch) + ;; uncomment and modify in case some elisp files are not found in load-path + ;; (add-to-list 'load-path "~/vc/ext/notmuch/emacs") - ;; add here stuff required to be configured *after* - ;; notmuch is loaded; + ;;; load notmuch + (require 'notmuch) - ;(setq user-mail-address (notmuch-user-primary-email) - ; user-full-name (notmuch-user-name)) + ;;; add here stuff required to be configured *after* + ;;; notmuch is loaded; - ; uncomment & modify if you want to use external smtp server to send mail - ; (setq smtpmail-smtp-server "smtp.server.tld" - ; message-send-mail-function 'message-smtpmail-send-it) + ;; uncomment & modify if you want to use external smtp server to send mail + ;; (setq smtpmail-smtp-server "smtp.server.tld" + ;; message-send-mail-function 'message-smtpmail-send-it) + ;; uncomment to debug smtp sending problems + ;; (setq smtpmail-debug-info t) -Then, adto `.emacs`: +Then, add to `.emacs`: (autoload 'notmuch "~/.emacs.d/my-notmuch" "notmuch mail" t) +## Initial cursor position in notmuch 0.15 hello window + +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. + +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 @@ -148,10 +188,10 @@ In notmuch versions up to 0.11.x (notmuch-show-add-tag "spam") (notmuch-show-remove-tag "inbox"))) -Starting from notmuch 0.12 (not released yet) 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: +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: (define-key notmuch-show-mode-map "S" (lambda () @@ -162,12 +202,27 @@ the above needs to be changed to this: You can do the same for threads in `notmuch-search-mode` by just replacing "show" with "search" in the called functions. -(Starting from notmuch 0.12 use `notmuch-search-tag-thread` instead) +Starting from notmuch 0.12 use `notmuch-search-tag-thread` instead: + + (define-key notmuch-search-mode-map "S" + (lambda () + "mark messages in thread 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: + + (define-key notmuch-search-mode-map "S" + (lambda () + "mark messages in thread as spam" + (interactive) + (notmuch-search-tag '("+spam" "-inbox")))) The definition above makes use of a lambda function, but you could also define a separate function first: - (defun notmuch-show-tag-spam() + (defun notmuch-show-tag-spam () "mark message as spam" (interactive) (notmuch-show-add-tag "spam") @@ -187,7 +242,7 @@ key: (notmuch-show-remove-tag "deleted") (notmuch-show-add-tag "deleted")))) -And version for notmuch 0.12 (not released yet) +And version for notmuch 0.12 (define-key notmuch-show-mode-map "d" (lambda () @@ -197,6 +252,34 @@ And version for notmuch 0.12 (not released yet) (if (member "deleted" (notmuch-show-get-tags)) "-deleted" "+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 @@ -216,12 +299,19 @@ And in search mode: 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\(notmuch-fcc-dirs\)\|\(message-directory\) @@ -239,14 +329,37 @@ 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 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 @@ -340,15 +453,19 @@ emacswiki. ## Address completion when composing -There are currently two solutions to this: +There are currently three solutions to this: + +### 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 three +candidates based on a search string. There are currently four available: * The python tool `notmuch_address.py` (`git clone @@ -364,25 +481,68 @@ available: 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). + files notmuch knows and search from that "database" is done by `fgrep(1)`. -You can perform tab-completion using any of these programs. Just add the following to your .emacs: + * 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: (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. +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 `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`. To sign outgoing mail by default, use the `message-setup-hook` -in your `.emacs` file: +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 +`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: ;; Sign messages by default. (add-hook 'message-setup-hook 'mml-secure-sign-pgpmime) @@ -403,3 +563,235 @@ part. 2011-02-02. Note that if you have the Debian package easypg installed, it will shadow the fixed version of easypg included with emacs. + +## 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`. + +## Resending (or bouncing) messages + +Add the following to your `.emacs` 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 `.emacs` 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 `.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)))) + + (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))))) + + (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 + + (define-key 'notmuch-show-mode-map "D" 'my-notmuch-show-view-as-patch)