;; This is an part of an emacs-based interface to the notmuch mail system.
+(eval-when-compile (require 'cl))
+
(defvar notmuch-command "notmuch"
"Command to run the notmuch binary.")
"Notmuch mail reader for Emacs."
:group 'mail)
+(defgroup notmuch-hello nil
+ "Overview of saved searches, tags, etc."
+ :group 'notmuch)
+
+(defgroup notmuch-search nil
+ "Searching and sorting mail."
+ :group 'notmuch)
+
+(defgroup notmuch-show nil
+ "Showing messages and threads."
+ :group 'notmuch)
+
+(defgroup notmuch-send nil
+ "Sending messages from Notmuch."
+ :group 'notmuch)
+
+(custom-add-to-group 'notmuch-send 'message 'custom-group)
+
+(defgroup notmuch-crypto nil
+ "Processing and display of cryptographic MIME parts."
+ :group 'notmuch)
+
+(defgroup notmuch-hooks nil
+ "Running custom code on well-defined occasions."
+ :group 'notmuch)
+
+(defgroup notmuch-external nil
+ "Running external commands from within Notmuch."
+ :group 'notmuch)
+
+(defgroup notmuch-faces nil
+ "Graphical attributes for displaying text"
+ :group 'notmuch)
+
(defcustom notmuch-search-oldest-first t
"Show the oldest mail first when searching."
:type 'boolean
- :group 'notmuch)
+ :group 'notmuch-search)
;;
+(defvar notmuch-search-history nil
+ "Variable to store notmuch searches history.")
+
(defcustom notmuch-saved-searches nil
"A list of saved searches to display."
:type '(alist :key-type string :value-type string)
- :group 'notmuch)
+ :group 'notmuch-hello)
(defvar notmuch-folders nil
"Deprecated name for what is now known as `notmuch-saved-searches'.")
"Return the user.primary_email value from the notmuch configuration."
(notmuch-config-get "user.primary_email"))
+(defun notmuch-user-other-email ()
+ "Return the user.other_email value (as a list) from the notmuch configuration."
+ (split-string (notmuch-config-get "user.other_email") "\n"))
+
(defun notmuch-kill-this-buffer ()
"Kill the current buffer."
(interactive)
(kill-buffer (current-buffer)))
+(defun notmuch-prettify-subject (subject)
+ ;; This function is used by `notmuch-search-process-filter' which
+ ;; requires that we not disrupt its' matching state.
+ (save-match-data
+ (if (and subject
+ (string-match "^[ \t]*$" subject))
+ "[No Subject]"
+ subject)))
+
+(defun notmuch-id-to-query (id)
+ "Return a query that matches the message with id ID."
+ (concat "id:\"" (replace-regexp-in-string "\"" "\"\"" id t t) "\""))
+
;;
(defun notmuch-common-do-stash (text)
;;
-;; XXX: This should be a generic function in emacs somewhere, not
-;; here.
-(defun point-invisible-p ()
- "Return whether the character at point is invisible.
-
-Here visibility is determined by `buffer-invisibility-spec' and
-the invisible property of any overlays for point. It doesn't have
-anything to do with whether point is currently being displayed
-within the current window."
- (let ((prop (get-char-property (point) 'invisible)))
- (if (eq buffer-invisibility-spec t)
- prop
- (or (memq prop buffer-invisibility-spec)
- (assq prop buffer-invisibility-spec)))))
+(defun notmuch-remove-if-not (predicate list)
+ "Return a copy of LIST with all items not satisfying PREDICATE removed."
+ (let (out)
+ (while list
+ (when (funcall predicate (car list))
+ (push (car list) out))
+ (setq list (cdr list)))
+ (nreverse out)))
+
+;; This lets us avoid compiling these replacement functions when emacs
+;; is sufficiently new enough to supply them alone. We do the macro
+;; treatment rather than just wrapping our defun calls in a when form
+;; specifically so that the compiler never sees the code on new emacs,
+;; (since the code is triggering warnings that we don't know how to get
+;; rid of.
+;;
+;; A more clever macro here would accept a condition and a list of forms.
+(defmacro compile-on-emacs-prior-to-23 (form)
+ "Conditionally evaluate form only on emacs < emacs-23."
+ (list 'when (< emacs-major-version 23)
+ form))
+
+(defun notmuch-split-content-type (content-type)
+ "Split content/type into 'content' and 'type'"
+ (split-string content-type "/"))
+
+(defun notmuch-match-content-type (t1 t2)
+ "Return t if t1 and t2 are matching content types, taking wildcards into account"
+ (let ((st1 (notmuch-split-content-type t1))
+ (st2 (notmuch-split-content-type t2)))
+ (if (or (string= (cadr st1) "*")
+ (string= (cadr st2) "*"))
+ ;; Comparison of content types should be case insensitive.
+ (string= (downcase (car st1)) (downcase (car st2)))
+ (string= (downcase t1) (downcase t2)))))
+
+(defvar notmuch-multipart/alternative-discouraged
+ '(
+ ;; Avoid HTML parts.
+ "text/html"
+ ;; multipart/related usually contain a text/html part and some associated graphics.
+ "multipart/related"
+ ))
+
+(defun notmuch-multipart/alternative-choose (types)
+ "Return a list of preferred types from the given list of types"
+ ;; Based on `mm-preferred-alternative-precedence'.
+ (let ((seq types))
+ (dolist (pref (reverse notmuch-multipart/alternative-discouraged))
+ (dolist (elem (copy-sequence seq))
+ (when (string-match pref elem)
+ (setq seq (nconc (delete elem seq) (list elem))))))
+ seq))
+
+(defun notmuch-parts-filter-by-type (parts type)
+ "Given a list of message parts, return a list containing the ones matching
+the given type."
+ (remove-if-not
+ (lambda (part) (notmuch-match-content-type (plist-get part :content-type) type))
+ parts))
+
+;; Helper for parts which are generally not included in the default
+;; JSON output.
+(defun notmuch-get-bodypart-internal (query part-number process-crypto)
+ (let ((args '("show" "--format=raw"))
+ (part-arg (format "--part=%s" part-number)))
+ (setq args (append args (list part-arg)))
+ (if process-crypto
+ (setq args (append args '("--decrypt"))))
+ (setq args (append args (list query)))
+ (with-temp-buffer
+ (let ((coding-system-for-read 'no-conversion))
+ (progn
+ (apply 'call-process (append (list notmuch-command nil (list t nil) nil) args))
+ (buffer-string))))))
+
+(defun notmuch-get-bodypart-content (msg part nth process-crypto)
+ (or (plist-get part :content)
+ (notmuch-get-bodypart-internal (notmuch-id-to-query (plist-get msg :id)) nth process-crypto)))
+
+;; Converts a plist of headers to an alist of headers. The input plist should
+;; have symbols of the form :Header as keys, and the resulting alist will have
+;; symbols of the form 'Header as keys.
+(defun notmuch-headers-plist-to-alist (plist)
+ (loop for (key value . rest) on plist by #'cddr
+ collect (cons (intern (substring (symbol-name key) 1)) value)))
;; Compatibility functions for versions of emacs before emacs 23.
;;
;; 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
;;
;; and under the GPL version 3 (or later) exactly as notmuch itself.
-(when (< emacs-major-version 23)
- (defun apply-partially (fun &rest args)
- "Return a function that is a partial application of FUN to ARGS.
+(compile-on-emacs-prior-to-23
+ (defun apply-partially (fun &rest args)
+ "Return a function that is a partial application of FUN to ARGS.
ARGS is a list of the first N arguments to pass to FUN.
The result is a new function which does the same as FUN, except that
the first N arguments are fixed at the values with which this function
was called."
- (lexical-let ((fun fun) (args1 args))
- (lambda (&rest args2) (apply fun (append args1 args2)))))
-
- (defun mouse-event-p (object)
- "Return non-nil if OBJECT is a mouse click event."
- (memq (event-basic-type object) '(mouse-1 mouse-2 mouse-3 mouse-movement))))
+ (lexical-let ((fun fun) (args1 args))
+ (lambda (&rest args2) (apply fun (append args1 args2))))))
+(compile-on-emacs-prior-to-23
+ (defun mouse-event-p (object)
+ "Return non-nil if OBJECT is a mouse click event."
+ (memq (event-basic-type object) '(mouse-1 mouse-2 mouse-3 mouse-movement))))
+;; This variable is used only buffer local, but it needs to be
+;; declared globally first to avoid compiler warnings.
+(defvar notmuch-show-process-crypto nil)
+(make-variable-buffer-local 'notmuch-show-process-crypto)
(provide 'notmuch-lib)