X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=emacs%2Fnotmuch.el;h=57e11400a526cf4b81433430d1ce0f65245f989d;hp=517c53a5d3784a5c2d8362f319b2e436ac640d19;hb=417274d698b6718621b9f5dec744ab169499f4e3;hpb=fec5622add1a4e9f305c16e96143439ee22a5c58 diff --git a/emacs/notmuch.el b/emacs/notmuch.el index 517c53a5..57e11400 100644 --- a/emacs/notmuch.el +++ b/emacs/notmuch.el @@ -53,6 +53,24 @@ (require 'notmuch-lib) (require 'notmuch-show) +(require 'notmuch-mua) +(require 'notmuch-hello) +(require 'notmuch-maildir-fcc) +(require 'notmuch-message) + +(defcustom notmuch-search-result-format + `(("date" . "%s ") + ("count" . "%-7s ") + ("authors" . "%-20s ") + ("subject" . "%s ") + ("tags" . "(%s)")) + "Search result formating. Supported fields are: + date, count, authors, subject, tags +For example: + (setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\) + \(\"subject\" . \"%s\"\)\)\)" + :type '(alist :key-type (string) :value-type (string)) + :group 'notmuch) (defun notmuch-select-tag-with-completion (prompt &rest search-terms) (let ((tag-list @@ -97,69 +115,6 @@ (mm-save-part p)))) mm-handle)) -(defun notmuch-reply (query-string) - (switch-to-buffer (generate-new-buffer "notmuch-draft")) - (call-process notmuch-command nil t nil "reply" query-string) - (message-insert-signature) - (goto-char (point-min)) - (if (re-search-forward "^$" nil t) - (progn - (insert "--text follows this line--") - (forward-line))) - (message-mode)) - -(defun notmuch-toggle-invisible-action (cite-button) - (let ((invis-spec (button-get cite-button 'invisibility-spec))) - (if (invisible-p invis-spec) - (remove-from-invisibility-spec invis-spec) - (add-to-invisibility-spec invis-spec) - )) - (force-window-update) - (redisplay t)) - -(define-button-type 'notmuch-button-citation-toggle-type 'help-echo "mouse-1, RET: Show citation" - :supertype 'notmuch-button-invisibility-toggle-type) -(define-button-type 'notmuch-button-signature-toggle-type 'help-echo "mouse-1, RET: Show signature" - :supertype 'notmuch-button-invisibility-toggle-type) -(define-button-type 'notmuch-button-body-toggle-type - 'help-echo "mouse-1, RET: Show message" - 'face 'notmuch-message-summary-face - :supertype 'notmuch-button-invisibility-toggle-type) - -(defun notmuch-fontify-headers () - (while (looking-at "[[:space:]]") - (forward-char)) - (if (looking-at "[Tt]o:") - (progn - (overlay-put (make-overlay (point) (re-search-forward ":")) - 'face 'message-header-name) - (overlay-put (make-overlay (point) (re-search-forward ".*$")) - 'face 'message-header-to)) - (if (looking-at "[B]?[Cc][Cc]:") - (progn - (overlay-put (make-overlay (point) (re-search-forward ":")) - 'face 'message-header-name) - (overlay-put (make-overlay (point) (re-search-forward ".*$")) - 'face 'message-header-cc)) - (if (looking-at "[Ss]ubject:") - (progn - (overlay-put (make-overlay (point) (re-search-forward ":")) - 'face 'message-header-name) - (overlay-put (make-overlay (point) (re-search-forward ".*$")) - 'face 'message-header-subject)) - (if (looking-at "[Ff]rom:") - (progn - (overlay-put (make-overlay (point) (re-search-forward ":")) - 'face 'message-header-name) - (overlay-put (make-overlay (point) (re-search-forward ".*$")) - 'face 'message-header-other)) - (if (looking-at "[Dd]ate:") - (progn - (overlay-put (make-overlay (point) (re-search-forward ":")) - 'face 'message-header-name) - (overlay-put (make-overlay (point) (re-search-forward ".*$")) - 'face 'message-header-other)))))))) - (defun notmuch-documentation-first-line (symbol) "Return the first line of the documentation string for SYMBOL." (let ((doc (documentation symbol))) @@ -230,24 +185,17 @@ For a mouse binding, return nil." (set-buffer-modified-p nil) (view-buffer (current-buffer) 'kill-buffer-if-not-modified)))) -(defgroup notmuch nil - "Notmuch mail reader for Emacs." - :group 'mail) - -(defcustom notmuch-search-hook nil +(defcustom notmuch-search-hook '(hl-line-mode) "List of functions to call when notmuch displays the search results." :type 'hook :options '(hl-line-mode) :group 'notmuch) -(defvar notmuch-search-authors-width 20 - "Number of columns to use to display authors in a notmuch-search buffer.") - (defvar notmuch-search-mode-map (let ((map (make-sparse-keymap))) (define-key map "?" 'notmuch-help) - (define-key map "q" 'kill-this-buffer) - (define-key map "x" 'kill-this-buffer) + (define-key map "q" 'notmuch-search-quit) + (define-key map "x" 'notmuch-search-quit) (define-key map (kbd "") 'notmuch-search-scroll-down) (define-key map "b" 'notmuch-search-scroll-down) (define-key map " " 'notmuch-search-scroll-up) @@ -256,10 +204,11 @@ For a mouse binding, return nil." (define-key map "p" 'notmuch-search-previous-thread) (define-key map "n" 'notmuch-search-next-thread) (define-key map "r" 'notmuch-search-reply-to-thread) - (define-key map "m" 'message-mail) + (define-key map "m" 'notmuch-mua-mail) (define-key map "s" 'notmuch-search) (define-key map "o" 'notmuch-search-toggle-order) (define-key map "=" 'notmuch-search-refresh-view) + (define-key map "G" 'notmuch-search-poll-and-refresh-view) (define-key map "t" 'notmuch-search-filter-by-tag) (define-key map "f" 'notmuch-search-filter) (define-key map [mouse-1] 'notmuch-search-show-thread) @@ -268,7 +217,6 @@ For a mouse binding, return nil." (define-key map "-" 'notmuch-search-remove-tag) (define-key map "+" 'notmuch-search-add-tag) (define-key map (kbd "RET") 'notmuch-search-show-thread) - (define-key map "F" 'notmuch-folder) map) "Keymap for \"notmuch search\" buffers.") (fset 'notmuch-search-mode-map notmuch-search-mode-map) @@ -276,11 +224,18 @@ For a mouse binding, return nil." (defvar notmuch-search-query-string) (defvar notmuch-search-target-thread) (defvar notmuch-search-target-line) -(defvar notmuch-search-oldest-first t - "Show the oldest mail first in the search-mode") +(defvar notmuch-search-continuation) (defvar notmuch-search-disjunctive-regexp "\\<[oO][rR]\\>") +(defun notmuch-search-quit () + "Exit the search buffer, calling any defined continuation function." + (interactive) + (let ((continuation notmuch-search-continuation)) + (kill-this-buffer) + (when continuation + (funcall continuation)))) + (defun notmuch-search-scroll-up () "Move forward through search results by one window's worth." (interactive) @@ -342,11 +297,6 @@ For a mouse binding, return nil." "Notmuch search mode face used to highligh tags." :group 'notmuch) -(defvar notmuch-tag-face-alist nil - "List containing the tag list that need to be highlighed") - -(defvar notmuch-search-font-lock-keywords nil) - ;;;###autoload (defun notmuch-search-mode () "Major mode displaying results of a notmuch search. @@ -378,31 +328,23 @@ Complete list of currently available key bindings: (make-local-variable 'notmuch-search-oldest-first) (make-local-variable 'notmuch-search-target-thread) (make-local-variable 'notmuch-search-target-line) + (set (make-local-variable 'notmuch-search-continuation) nil) (set (make-local-variable 'scroll-preserve-screen-position) t) (add-to-invisibility-spec 'notmuch-search) (use-local-map notmuch-search-mode-map) (setq truncate-lines t) (setq major-mode 'notmuch-search-mode mode-name "notmuch-search") - (setq buffer-read-only t) - (if (not notmuch-tag-face-alist) - (add-to-list 'notmuch-search-font-lock-keywords (list - "(\\([^()]*\\))$" '(1 'notmuch-tag-face))) - (let ((notmuch-search-tags (mapcar 'car notmuch-tag-face-alist))) - (loop for notmuch-search-tag in notmuch-search-tags - do (add-to-list 'notmuch-search-font-lock-keywords (list - (concat "([^)]*\\(" notmuch-search-tag "\\)[^)]*)$") - `(1 ,(cdr (assoc notmuch-search-tag notmuch-tag-face-alist)))))))) - (set (make-local-variable 'font-lock-defaults) - '(notmuch-search-font-lock-keywords t))) + (setq buffer-read-only t)) (defun notmuch-search-properties-in-region (property beg end) (save-excursion (let ((output nil) - (last-line (line-number-at-pos end))) + (last-line (line-number-at-pos end)) + (max-line (- (line-number-at-pos (point-max)) 2))) (goto-char beg) (beginning-of-line) - (while (<= (line-number-at-pos) last-line) + (while (<= (line-number-at-pos) (min last-line max-line)) (setq output (cons (get-text-property (point) property) output)) (forward-line 1)) output))) @@ -435,25 +377,26 @@ Complete list of currently available key bindings: "Display the currently selected thread." (interactive) (let ((thread-id (notmuch-search-find-thread-id)) - (subject (notmuch-search-find-subject)) - buffer-name) - (when (string-match "^[ \t]*$" subject) - (setq subject "[No Subject]")) - (setq buffer-name (concat "*" - (truncate-string-to-width subject 32 nil nil t) - "*")) + (subject (notmuch-search-find-subject))) (if (> (length thread-id) 0) (notmuch-show thread-id (current-buffer) notmuch-search-query-string - buffer-name) + ;; name the buffer based on notmuch-search-find-subject + (if (string-match "^[ \t]*$" subject) + "[No Subject]" + (truncate-string-to-width + (concat "*" + (truncate-string-to-width subject 32 nil nil t) + "*") + 32 nil nil t))) (error "End of search results")))) (defun notmuch-search-reply-to-thread () "Begin composing a reply to the entire current thread in a new buffer." (interactive) (let ((message-id (notmuch-search-find-thread-id))) - (notmuch-reply message-id))) + (notmuch-mua-reply message-id))) (defun notmuch-call-notmuch-process (&rest args) "Synchronously invoke \"notmuch\" with the given list of arguments. @@ -483,7 +426,8 @@ and will also appear in a buffer named \"*Notmuch errors*\"." (backward-char) (let ((end (point))) (delete-region beg end) - (insert (mapconcat 'identity tags " ")))))) + (insert (propertize (mapconcat 'identity tags " ") + 'font-lock-face 'notmuch-tag-face)))))) (defun notmuch-search-get-tags () (save-excursion @@ -497,38 +441,39 @@ and will also appear in a buffer named \"*Notmuch errors*\"." (defun notmuch-search-get-tags-region (beg end) (save-excursion (let ((output nil) - (last-line (line-number-at-pos end))) + (last-line (line-number-at-pos end)) + (max-line (- (line-number-at-pos (point-max)) 2))) (goto-char beg) - (while (<= (line-number-at-pos) last-line) + (while (<= (line-number-at-pos) (min last-line max-line)) (setq output (append output (notmuch-search-get-tags))) (forward-line 1)) output))) (defun notmuch-search-add-tag-thread (tag) - (notmuch-call-notmuch-process "tag" (concat "+" tag) (notmuch-search-find-thread-id)) - (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<)))) + (notmuch-search-add-tag-region tag (point) (point))) (defun notmuch-search-add-tag-region (tag beg end) (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or "))) (notmuch-call-notmuch-process "tag" (concat "+" tag) search-id-string) (save-excursion - (let ((last-line (line-number-at-pos end))) + (let ((last-line (line-number-at-pos end)) + (max-line (- (line-number-at-pos (point-max)) 2))) (goto-char beg) - (while (<= (line-number-at-pos) last-line) + (while (<= (line-number-at-pos) (min last-line max-line)) (notmuch-search-set-tags (delete-dups (sort (cons tag (notmuch-search-get-tags)) 'string<))) (forward-line)))))) (defun notmuch-search-remove-tag-thread (tag) - (notmuch-call-notmuch-process "tag" (concat "-" tag) (notmuch-search-find-thread-id)) - (notmuch-search-set-tags (delete tag (notmuch-search-get-tags)))) + (notmuch-search-remove-tag-region tag (point) (point))) (defun notmuch-search-remove-tag-region (tag beg end) (let ((search-id-string (mapconcat 'identity (notmuch-search-find-thread-id-region beg end) " or "))) (notmuch-call-notmuch-process "tag" (concat "-" tag) search-id-string) (save-excursion - (let ((last-line (line-number-at-pos end))) + (let ((last-line (line-number-at-pos end)) + (max-line (- (line-number-at-pos (point-max)) 2))) (goto-char beg) - (while (<= (line-number-at-pos) last-line) + (while (<= (line-number-at-pos) (min last-line max-line)) (notmuch-search-set-tags (delete tag (notmuch-search-get-tags))) (forward-line)))))) @@ -631,6 +576,32 @@ matching will be applied." (t (setq tags-faces (cdr tags-faces))))))))) +(defun notmuch-search-insert-field (field date count authors subject tags) + (cond + ((string-equal field "date") + (insert (format (cdr (assoc field notmuch-search-result-format)) date))) + ((string-equal field "count") + (insert (format (cdr (assoc field notmuch-search-result-format)) count))) + ((string-equal field "authors") + (insert (let* ((format-string (cdr (assoc field notmuch-search-result-format))) + (formatted-sample (format format-string "")) + (formatted-authors (format format-string authors))) + (if (> (length formatted-authors) + (length formatted-sample)) + (concat (substring authors 0 (- (length formatted-sample) 4)) "... ") + formatted-authors)))) + ((string-equal field "subject") + (insert (format (cdr (assoc field notmuch-search-result-format)) subject))) + ((string-equal field "tags") + (insert (concat "(" (propertize tags 'font-lock-face 'notmuch-tag-face) ")"))))) + +(defun notmuch-search-show-result (date count authors subject tags) + (let ((fields) (field)) + (setq fields (mapcar 'car notmuch-search-result-format)) + (loop for field in fields + do (notmuch-search-insert-field field date count authors subject tags))) + (insert "\n")) + (defun notmuch-search-process-filter (proc string) "Process and filter the output of \"notmuch search\"" (let ((buffer (process-buffer proc)) @@ -647,16 +618,12 @@ matching will be applied." (date (match-string 2 string)) (count (match-string 3 string)) (authors (match-string 4 string)) - (authors-length (length authors)) (subject (match-string 5 string)) (tags (match-string 6 string)) (tag-list (if tags (save-match-data (split-string tags))))) - (if (> authors-length notmuch-search-authors-width) - (set 'authors (concat (substring authors 0 (- notmuch-search-authors-width 3)) "..."))) (goto-char (point-max)) - (let ((beg (point-marker)) - (format-string (format "%%s %%-7s %%-%ds %%s (%%s)\n" notmuch-search-authors-width))) - (insert (format format-string date count authors subject tags)) + (let ((beg (point-marker))) + (notmuch-search-show-result date count authors subject tags) (notmuch-search-color-line beg (point-marker) tag-list) (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id) (put-text-property beg (point-marker) 'notmuch-search-authors authors) @@ -694,8 +661,29 @@ characters as well as `_.+-'. (apply 'notmuch-call-notmuch-process "tag" (append action-split (list notmuch-search-query-string) nil)))) +(defun notmuch-search-buffer-title (query) + "Returns the title for a buffer with notmuch search results." + (let* ((saved-search (rassoc-if (lambda (key) + (string-match (concat "^" (regexp-quote key)) + query)) + (reverse (notmuch-saved-searches)))) + (saved-search-name (car saved-search)) + (saved-search-query (cdr saved-search))) + (cond ((and saved-search (equal saved-search-query query)) + ;; Query is the same as saved search (ignoring case) + (concat "*notmuch-saved-search-" saved-search-name "*")) + (saved-search + (concat "*notmuch-search-" + (replace-regexp-in-string (concat "^" (regexp-quote saved-search-query)) + (concat "[ " saved-search-name " ]") + query) + "*")) + (t + (concat "*notmuch-search-" query "*")) + ))) + ;;;###autoload -(defun notmuch-search (query &optional oldest-first target-thread target-line) +(defun notmuch-search (query &optional oldest-first target-thread target-line continuation) "Run \"notmuch search\" with the given query string and display results. The optional parameters are used as follows: @@ -706,13 +694,14 @@ The optional parameters are used as follows: target-line: The line number to move to if the target thread does not appear in the search results." (interactive "sNotmuch search: ") - (let ((buffer (get-buffer-create (concat "*notmuch-search-" query "*")))) + (let ((buffer (get-buffer-create (notmuch-search-buffer-title query)))) (switch-to-buffer buffer) (notmuch-search-mode) (set 'notmuch-search-query-string query) (set 'notmuch-search-oldest-first oldest-first) (set 'notmuch-search-target-thread target-thread) (set 'notmuch-search-target-line target-line) + (set 'notmuch-search-continuation continuation) (let ((proc (get-buffer-process (current-buffer))) (inhibit-read-only t)) (if proc @@ -741,11 +730,40 @@ same relative position within the new buffer." (let ((target-line (line-number-at-pos)) (oldest-first notmuch-search-oldest-first) (target-thread (notmuch-search-find-thread-id)) - (query notmuch-search-query-string)) + (query notmuch-search-query-string) + (continuation notmuch-search-continuation)) (kill-this-buffer) - (notmuch-search query oldest-first target-thread target-line) - (goto-char (point-min)) - )) + (notmuch-search query oldest-first target-thread target-line continuation) + (goto-char (point-min)))) + +(defcustom notmuch-poll-script "" + "An external script to incorporate new mail into the notmuch database. + +If this variable is non empty, then it should name a script to be +invoked by `notmuch-search-poll-and-refresh-view' and +`notmuch-hello-poll-and-update' (each have a default keybinding +of 'G'). The 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" + :type 'string + :group 'notmuch) + +(defun notmuch-poll () + "Run external script to import mail. + +Invokes `notmuch-poll-script' if it is not set to an empty string." + (interactive) + (if (not (string= notmuch-poll-script "")) + (call-process notmuch-poll-script nil nil))) + +(defun notmuch-search-poll-and-refresh-view () + "Invoke `notmuch-poll' to import mail, then refresh the current view." + (interactive) + (notmuch-poll) + (notmuch-search-refresh-view)) (defun notmuch-search-toggle-order () "Toggle the current search order. @@ -771,8 +789,12 @@ search." Runs a new search matching only messages that match both the current search results AND the additional query string provided." (interactive "sFilter search: ") - (let ((grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query) (concat "( " query " )") query))) - (notmuch-search (concat notmuch-search-query-string " and " grouped-query) notmuch-search-oldest-first))) + (let ((grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query) + (concat "( " query " )") + query))) + (notmuch-search (if (string= notmuch-search-query-string "*") + grouped-query + (concat notmuch-search-query-string " and " grouped-query)) notmuch-search-oldest-first))) (defun notmuch-search-filter-by-tag (tag) "Filter the current search results based on a single tag. @@ -785,152 +807,10 @@ current search results AND that are tagged with the given tag." ;;;###autoload (defun notmuch () - "Run notmuch to display all mail with tag of 'inbox'" - (interactive) - (notmuch-search "tag:inbox" notmuch-search-oldest-first)) - -(setq mail-user-agent 'message-user-agent) - -(defvar notmuch-folder-mode-map - (let ((map (make-sparse-keymap))) - (define-key map "?" 'notmuch-help) - (define-key map "x" 'kill-this-buffer) - (define-key map "q" 'kill-this-buffer) - (define-key map "m" 'message-mail) - (define-key map "e" 'notmuch-folder-show-empty-toggle) - (define-key map ">" 'notmuch-folder-last) - (define-key map "<" 'notmuch-folder-first) - (define-key map "=" 'notmuch-folder) - (define-key map "s" 'notmuch-search) - (define-key map [mouse-1] 'notmuch-folder-show-search) - (define-key map (kbd "RET") 'notmuch-folder-show-search) - (define-key map " " 'notmuch-folder-show-search) - (define-key map "p" 'notmuch-folder-previous) - (define-key map "n" 'notmuch-folder-next) - map) - "Keymap for \"notmuch folder\" buffers.") - -(fset 'notmuch-folder-mode-map notmuch-folder-mode-map) - -(defcustom notmuch-folders (quote (("inbox" . "tag:inbox") ("unread" . "tag:unread"))) - "List of searches for the notmuch folder view" - :type '(alist :key-type (string) :value-type (string)) - :group 'notmuch) - -(defun notmuch-folder-mode () - "Major mode for showing notmuch 'folders'. - -This buffer contains a list of message counts returned by a -customizable set of searches of your email archives. Each line in -the buffer shows the name of a saved search and the resulting -message count. - -Pressing RET on any line opens a search window containing the -results for the saved search on that line. - -Here is an example of how the search list could be -customized, (the following text would be placed in your ~/.emacs -file): - -(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\"). - -Currently available key bindings: - -\\{notmuch-folder-mode-map}" - (interactive) - (kill-all-local-variables) - (use-local-map 'notmuch-folder-mode-map) - (setq truncate-lines t) - (hl-line-mode 1) - (setq major-mode 'notmuch-folder-mode - mode-name "notmuch-folder") - (setq buffer-read-only t)) - -(defun notmuch-folder-next () - "Select the next folder in the list." - (interactive) - (forward-line 1) - (if (eobp) - (forward-line -1))) - -(defun notmuch-folder-previous () - "Select the previous folder in the list." - (interactive) - (forward-line -1)) - -(defun notmuch-folder-first () - "Select the first folder in the list." + "Run notmuch and display saved searches, known tags, etc." (interactive) - (goto-char (point-min))) - -(defun notmuch-folder-last () - "Select the last folder in the list." - (interactive) - (goto-char (point-max)) - (forward-line -1)) + (notmuch-hello)) -(defun notmuch-folder-count (search) - (car (process-lines notmuch-command "count" search))) - -(defvar notmuch-folder-show-empty t - "Whether `notmuch-folder-mode' should display empty folders.") - -(defun notmuch-folder-show-empty-toggle () - "Toggle the listing of empty folders" - (interactive) - (setq notmuch-folder-show-empty (not notmuch-folder-show-empty)) - (notmuch-folder)) - -(defun notmuch-folder-add (folders) - (if folders - (let* ((name (car (car folders))) - (inhibit-read-only t) - (search (cdr (car folders))) - (count (notmuch-folder-count search))) - (if (or notmuch-folder-show-empty - (not (equal count "0"))) - (progn - (insert name) - (indent-to 16 1) - (insert count) - (insert "\n") - ) - ) - (notmuch-folder-add (cdr folders))))) - -(defun notmuch-folder-find-name () - (save-excursion - (beginning-of-line) - (let ((beg (point))) - (re-search-forward "\\([ \t]*[^ \t]+\\)") - (filter-buffer-substring (match-beginning 1) (match-end 1))))) - -(defun notmuch-folder-show-search (&optional folder) - "Show a search window for the search related to the specified folder." - (interactive) - (if (null folder) - (setq folder (notmuch-folder-find-name))) - (let ((search (assoc folder notmuch-folders))) - (if search - (notmuch-search (cdr search) notmuch-search-oldest-first)))) - -;;;###autoload -(defun notmuch-folder () - "Show the notmuch folder view and update the displayed counts." - (interactive) - (let ((buffer (get-buffer-create "*notmuch-folders*"))) - (switch-to-buffer buffer) - (let ((inhibit-read-only t) - (n (line-number-at-pos))) - (erase-buffer) - (notmuch-folder-mode) - (notmuch-folder-add notmuch-folders) - (goto-char (point-min)) - (goto-line n)))) +(setq mail-user-agent 'notmuch-user-agent) (provide 'notmuch)