X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=emacs%2Fnotmuch-lib.el;h=1e166c6afce53a501689c616f6478d7d3d496412;hp=3eedd7103bc20e8beb02045e17aad45194a11833;hb=ce44657c9e4919b1e4bbbbbe37dbd72257d40736;hpb=8141555d2540633c9b741993b92d6b9b3030fd83 diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el index 3eedd710..1e166c6a 100644 --- a/emacs/notmuch-lib.el +++ b/emacs/notmuch-lib.el @@ -25,8 +25,8 @@ (require 'mm-decode) (require 'cl) -(defvar notmuch-command "notmuch" - "Command to run the notmuch binary.") +(autoload 'notmuch-jump-search "notmuch-jump" + "Jump to a saved search by shortcut key." t) (defgroup notmuch nil "Notmuch mail reader for Emacs." @@ -66,6 +66,16 @@ "Graphical attributes for displaying text" :group 'notmuch) +(defcustom notmuch-command "notmuch" + "Name of the notmuch binary. + +This can be a relative or absolute path to the notmuch binary. +If this is a relative path, it will be searched for in all of the +directories given in `exec-path' (which is, by default, based on +$PATH)." + :type 'string + :group 'notmuch-external) + (defcustom notmuch-search-oldest-first t "Show the oldest mail first when searching. @@ -77,7 +87,11 @@ search." :group 'notmuch-search) (defcustom notmuch-poll-script nil - "An external script to incorporate new mail into the notmuch database. + "[Deprecated] Command to run to incorporate new mail into the notmuch database. + +This option has been deprecated in favor of \"notmuch new\" +hooks (see man notmuch-hooks). To change the path to the notmuch +binary, customize `notmuch-command'. This variable controls the action invoked by `notmuch-poll-and-refresh-this-buffer' (bound by default to 'G') @@ -93,10 +107,7 @@ 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 - -Note that the recommended way of achieving the same is using -\"notmuch new\" hooks." +3. Invoke one or more \"notmuch tag\" commands to classify the mail" :type '(choice (const :tag "notmuch new" nil) (const :tag "Disabled" "") (string :tag "Custom script")) @@ -107,12 +118,6 @@ Note that the recommended way of achieving the same is using (defvar notmuch-search-history nil "Variable to store notmuch searches history.") -(defcustom notmuch-saved-searches '(("inbox" . "tag:inbox") - ("unread" . "tag:unread")) - "A list of saved searches to display." - :type '(alist :key-type string :value-type string) - :group 'notmuch-hello) - (defcustom notmuch-archive-tags '("-inbox") "List of tag changes to apply to a message or a thread when it is archived. @@ -136,6 +141,7 @@ For example, if you wanted to remove an \"inbox\" tag and add an (define-key map "m" 'notmuch-mua-new-mail) (define-key map "=" 'notmuch-refresh-this-buffer) (define-key map "G" 'notmuch-poll-and-refresh-this-buffer) + (define-key map "j" 'notmuch-jump-search) map) "Keymap shared by all notmuch modes.") @@ -168,6 +174,24 @@ Otherwise the output will be returned" (notmuch-check-exit-status status (cons notmuch-command args) output) output))) +(defvar notmuch--cli-sane-p nil + "Cache whether the CLI seems to be configured sanely.") + +(defun notmuch-cli-sane-p () + "Return t if the cli seems to be configured sanely." + (unless notmuch--cli-sane-p + (let ((status (call-process notmuch-command nil nil nil + "config" "get" "user.primary_email"))) + (setq notmuch--cli-sane-p (= status 0)))) + notmuch--cli-sane-p) + +(defun notmuch-assert-cli-sane () + (unless (notmuch-cli-sane-p) + (notmuch-logged-error + "notmuch cli seems misconfigured or unconfigured." +"Perhaps you haven't run \"notmuch setup\" yet? Try running this +on the command line, and then retry your notmuch command"))) + (defun notmuch-version () "Return a string with the notmuch version number." (let ((long-string @@ -180,8 +204,13 @@ Otherwise the output will be returned" (defun notmuch-config-get (item) "Return a value from the notmuch configuration." - ;; Trim off the trailing newline - (substring (notmuch-command-to-string "config" "get" item) 0 -1)) + (let* ((val (notmuch-command-to-string "config" "get" item)) + (len (length val))) + ;; Trim off the trailing newline (if the value is empty or not + ;; configured, there will be no newline) + (if (and (> len 0) (= (aref val (- len 1)) ?\n)) + (substring val 0 -1) + val))) (defun notmuch-database-path () "Return the database.path value from the notmuch configuration." @@ -197,7 +226,7 @@ Otherwise the output will be returned" (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")) + (split-string (notmuch-config-get "user.other_email") "\n" t)) (defun notmuch-poll () "Run \"notmuch new\" or an external script to import mail. @@ -231,7 +260,8 @@ depending on the value of `notmuch-poll-script'." "Given a prefix key code, return a human-readable string representation. This is basically just `format-kbd-macro' but we also convert ESC to M-." - (let ((desc (format-kbd-macro (vector key)))) + (let* ((key-vector (if (vectorp key) key (vector key))) + (desc (format-kbd-macro key-vector))) (if (string= desc "ESC") "M-" (concat desc " ")))) @@ -260,6 +290,21 @@ It does not prepend if ACTUAL-KEY is already listed in TAIL." tail))) tail) +(defun notmuch-describe-remaps (remap-keymap ua-keys base-keymap prefix tail) + ;; Remappings are represented as a binding whose first "event" is + ;; 'remap. Hence, if the keymap has any remappings, it will have a + ;; binding whose "key" is 'remap, and whose "binding" is itself a + ;; keymap that maps not from keys to commands, but from old (remapped) + ;; functions to the commands to use in their stead. + (map-keymap + (lambda (command binding) + (mapc + (lambda (actual-key) + (setq tail (notmuch-describe-key actual-key binding prefix ua-keys tail))) + (where-is-internal command base-keymap))) + remap-keymap) + tail) + (defun notmuch-describe-keymap (keymap ua-keys base-keymap &optional prefix tail) "Return a list of cons cells, each describing one binding in KEYMAP. @@ -276,8 +321,11 @@ prefix argument. PREFIX and TAIL are used internally." (cond ((mouse-event-p key) nil) ((keymapp binding) (setq tail - (notmuch-describe-keymap - binding ua-keys base-keymap (notmuch-prefix-key-description key) tail))) + (if (eq key 'remap) + (notmuch-describe-remaps + binding ua-keys base-keymap prefix tail) + (notmuch-describe-keymap + binding ua-keys base-keymap (notmuch-prefix-key-description key) tail)))) (binding (setq tail (notmuch-describe-key (vector key) binding prefix ua-keys tail))))) keymap) @@ -319,6 +367,28 @@ of its command symbol." (set-buffer-modified-p nil) (view-buffer (current-buffer) 'kill-buffer-if-not-modified)))) +(defun notmuch-subkeymap-help () + "Show help for a subkeymap." + (interactive) + (let* ((key (this-command-keys-vector)) + (prefix (make-vector (1- (length key)) nil)) + (i 0)) + (while (< i (length prefix)) + (aset prefix i (aref key i)) + (setq i (1+ i))) + + (let* ((subkeymap (key-binding prefix)) + (ua-keys (where-is-internal 'universal-argument nil t)) + (prefix-string (notmuch-prefix-key-description prefix)) + (desc-alist (notmuch-describe-keymap subkeymap ua-keys subkeymap prefix-string)) + (desc-list (mapcar (lambda (arg) (concat (car arg) "\t" (cdr arg))) desc-alist)) + (desc (mapconcat #'identity desc-list "\n"))) + (with-help-window (help-buffer) + (with-current-buffer standard-output + (insert "\nPress 'q' to quit this window.\n\n") + (insert desc))) + (pop-to-buffer (help-buffer))))) + (defvar notmuch-buffer-refresh-function nil "Function to call to refresh the current buffer.") (make-variable-buffer-local 'notmuch-buffer-refresh-function) @@ -362,7 +432,10 @@ user-friendly queries." (save-match-data (if (or (equal term "") - (string-match "[ ()]\\|^\"" term)) + ;; To be pessimistic, only pass through terms composed + ;; entirely of ASCII printing characters other than ", (, + ;; and ). + (string-match "[^!#-'*-~]" term)) ;; Requires escaping (concat "\"" (replace-regexp-in-string "\"" "\"\"" term t t) "\"") term))) @@ -403,6 +476,15 @@ This replaces spaces, percents, and double quotes in STR with (setq list (cdr list))) (nreverse out))) +(defun notmuch-plist-delete (plist property) + (let* ((xplist (cons nil plist)) + (pred xplist)) + (while (cdr pred) + (when (eq (cadr pred) property) + (setcdr pred (cdddr pred))) + (setq pred (cddr pred))) + (cdr xplist))) + (defun notmuch-split-content-type (content-type) "Split content/type into 'content' and 'type'" (split-string content-type "/")) @@ -457,9 +539,10 @@ the given type." (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) +(defun notmuch-get-bodypart-content (msg part process-crypto) (or (plist-get part :content) - (notmuch-get-bodypart-internal (notmuch-id-to-query (plist-get msg :id)) nth process-crypto))) + (notmuch-get-bodypart-internal (notmuch-id-to-query (plist-get msg :id)) + (plist-get part :id) process-crypto))) ;; Workaround: The call to `mm-display-part' below triggers a bug in ;; Emacs 24 if it attempts to use the shr renderer to display an HTML @@ -472,9 +555,10 @@ the given type." (if (>= emacs-major-version 24) (defadvice mm-shr (before load-gnus-arts activate) (require 'gnus-art nil t) - (ad-disable-advice 'mm-shr 'before 'load-gnus-arts))) + (ad-disable-advice 'mm-shr 'before 'load-gnus-arts) + (ad-activate 'mm-shr))) -(defun notmuch-mm-display-part-inline (msg part nth content-type process-crypto) +(defun notmuch-mm-display-part-inline (msg part content-type process-crypto) "Use the mm-decode/mm-view functions to display a part in the current buffer, if possible." (let ((display-buffer (current-buffer))) @@ -490,7 +574,7 @@ current buffer, if possible." ;; test whether we are able to inline it (which includes both ;; capability and suitability tests). (when (mm-inlined-p handle) - (insert (notmuch-get-bodypart-content msg part nth process-crypto)) + (insert (notmuch-get-bodypart-content msg part process-crypto)) (when (mm-inlinable-p handle) (set-buffer display-buffer) (mm-display-part handle) @@ -513,23 +597,32 @@ single element face list." face (list face))) -(defun notmuch-combine-face-text-property (start end face &optional below object) - "Combine FACE into the 'face text property between START and END. +(defun notmuch-apply-face (object face &optional below start end) + "Combine FACE into the 'face text property of OBJECT between START and END. This function combines FACE with any existing faces between START -and END in OBJECT (which defaults to the current buffer). -Attributes specified by FACE take precedence over existing -attributes unless BELOW is non-nil. FACE must be a face name (a -symbol or string), a property list of face attributes, or a list -of these. For convenience when applied to strings, this returns -OBJECT." +and END in OBJECT. Attributes specified by FACE take precedence +over existing attributes unless BELOW is non-nil. + +OBJECT may be a string, a buffer, or nil (which means the current +buffer). If object is a string, START and END are 0-based; +otherwise they are buffer positions (integers or markers). FACE +must be a face name (a symbol or string), a property list of face +attributes, or a list of these. If START and/or END are omitted, +they default to the beginning/end of OBJECT. For convenience +when applied to strings, this returns OBJECT." ;; A face property can have three forms: a face name (a string or ;; symbol), a property list, or a list of these two forms. In the ;; list case, the faces will be combined, with the earlier faces ;; taking precedent. Here we canonicalize everything to list form ;; to make it easy to combine. - (let ((pos start) + (let ((pos (cond (start start) + ((stringp object) 0) + (t 1))) + (end (cond (end end) + ((stringp object) (length object)) + (t (1+ (buffer-size object))))) (face-list (notmuch-face-ensure-list-form face))) (while (< pos end) (let* ((cur (get-text-property pos 'face object)) @@ -542,14 +635,6 @@ OBJECT." (setq pos next)))) object) -(defun notmuch-combine-face-text-property-string (string face &optional below) - (notmuch-combine-face-text-property - 0 - (length string) - face - below - string)) - (defun notmuch-map-text-property (start end prop func &optional object) "Transform text property PROP using FUNC.