X-Git-Url: https://git.notmuchmail.org/git?p=notmuch;a=blobdiff_plain;f=emacs%2Fnotmuch-address.el;h=64887a43fe652b04332e0e3ed9b15577656cc9f8;hp=8a6d299cb3061bc7234c932a59a73f7e6da87e21;hb=HEAD;hpb=2156517d902e6013463a5f5f79225741e7f54307 diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index 8a6d299c..f756254c 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -1,4 +1,4 @@ -;;; notmuch-address.el --- address completion with notmuch +;;; notmuch-address.el --- address completion with notmuch -*- lexical-binding: t -*- ;; ;; Copyright © David Edmondson ;; @@ -25,9 +25,11 @@ (require 'notmuch-parser) (require 'notmuch-lib) (require 'notmuch-company) -;; + (declare-function company-manual-begin "company") +;;; Cache internals + (defvar notmuch-address-last-harvest 0 "Time of last address harvest.") @@ -36,9 +38,9 @@ This variable is set by calling `notmuch-address-harvest'.") (defvar notmuch-address-full-harvest-finished nil - "t indicates that full completion address harvesting has been finished. -Use notmuch-address--harvest-ready to access as that will load a -saved hash if necessary (and available).") + "Whether full completion address harvesting has finished. +Use `notmuch-address--harvest-ready' to access as that will load +a saved hash if necessary (and available).") (defun notmuch-address--harvest-ready () "Return t if there is a full address hash available. @@ -47,24 +49,33 @@ If the hash is not present it attempts to load a saved hash." (or notmuch-address-full-harvest-finished (notmuch-address--load-address-hash))) +;;; Options + (defcustom notmuch-address-command 'internal "Determines how address completion candidates are generated. -If it is a string then that string should be an external program -which must take a single argument (searched string) and output a -list of completion candidates, one per line. +If this is a string, then that string should be an external +program, which must take a single argument (searched string) +and output a list of completion candidates, one per line. + +If this is the symbol `internal', then an implementation is used +that relies on the \"notmuch address\" command, but does not use +any third-party (i.e. \"external\") programs. -Alternatively, it can be the symbol `internal', in which case -internal completion is used; the variable -`notmuch-address-internal-completion' can be used to customize -this case. +If this is the symbol `as-is', then Notmuch does not modify the +value of `message-completion-alist'. This option has to be set to +this value before `notmuch' is loaded, otherwise the modification +to `message-completion-alist' may already have taken place. This +setting obviously does not prevent `message-completion-alist' +from being modified at all; the user or some third-party package +may still modify it. -Finally, if this variable is nil then address completion is -disabled." +Finally, if this is nil, then address completion is disabled." :type '(radio - (const :tag "Use internal address completion" internal) - (const :tag "Disable address completion" nil) - (string :tag "Use external completion command")) + (const :tag "Use internal address completion" internal) + (string :tag "Use external completion command") + (const :tag "Disable address completion" nil) + (const :tag "Use default or third-party mechanism" as-is)) :group 'notmuch-send :group 'notmuch-address :group 'notmuch-external) @@ -133,9 +144,16 @@ matching `notmuch-address-completion-headers-regexp'." :group 'notmuch-address :group 'notmuch-hooks) +(defcustom notmuch-address-use-company t + "If available, use company mode for address completion." + :type 'boolean + :group 'notmuch-send + :group 'notmuch-address) + +;;; Setup + (defun notmuch-address-selection-function (prompt collection initial-input) - "Call (`completing-read' - PROMPT COLLECTION nil nil INITIAL-INPUT 'notmuch-address-history)" + "Default address selection function: delegate to completing read." (completing-read prompt collection nil nil initial-input 'notmuch-address-history)) @@ -147,22 +165,14 @@ matching `notmuch-address-completion-headers-regexp'." (defun notmuch-address-message-insinuate () (message "calling notmuch-address-message-insinuate is no longer needed")) -(defcustom notmuch-address-use-company t - "If available, use company mode for address completion." - :type 'boolean - :group 'notmuch-send - :group 'notmuch-address) - (defun notmuch-address-setup () - (let* ((setup-company (and notmuch-address-use-company - (require 'company nil t))) - (pair (cons notmuch-address-completion-headers-regexp - #'notmuch-address-expand-name))) - (when setup-company + (unless (eq notmuch-address-command 'as-is) + (when (and notmuch-address-use-company + (require 'company nil t)) (notmuch-company-setup)) - (unless (member pair message-completion-alist) - (setq message-completion-alist - (push pair message-completion-alist))))) + (cl-pushnew (cons notmuch-address-completion-headers-regexp + #'notmuch-address-expand-name) + message-completion-alist :test #'equal))) (defun notmuch-address-toggle-internal-completion () "Toggle use of internal completion for current buffer. @@ -178,12 +188,14 @@ toggles the setting in this buffer." (kill-local-variable 'company-idle-delay) (setq-local company-idle-delay nil)))) +;;; Completion + (defun notmuch-address-matching (substring) "Returns a list of completion candidates matching SUBSTRING. The candidates are taken from `notmuch-address-completions'." (let ((candidates) (re (regexp-quote substring))) - (maphash (lambda (key val) + (maphash (lambda (key _val) (when (string-match re key) (push key candidates))) notmuch-address-completions) @@ -204,7 +216,7 @@ requiring external commands." ;; harvest if necessary. (notmuch-address-harvest-trigger))) (t - (process-lines notmuch-address-command original)))) + (notmuch--process-lines notmuch-address-command original)))) (defun notmuch-address-expand-name () (cond @@ -231,14 +243,8 @@ requiring external commands." (t (funcall notmuch-address-selection-function (format "Address (%s matches): " num-options) - ;; We put the first match as the initial - ;; input; we put all the matches as - ;; possible completions, moving the - ;; first match to the end of the list - ;; makes cursor up/down in the list work - ;; better. - (append (cdr options) (list (car options))) - (car options)))))) + options + orig))))) (if chosen (progn (push chosen notmuch-address-history) @@ -250,31 +256,11 @@ requiring external commands." (ding)))) (t nil))) -;; Copied from `w3m-which-command'. -(defun notmuch-address-locate-command (command) - "Return non-nil if `command' is an executable either on -`exec-path' or an absolute pathname." - (and (stringp command) - (if (and (file-name-absolute-p command) - (file-executable-p command)) - command - (setq command (file-name-nondirectory command)) - (catch 'found-command - (let (bin) - (dolist (dir exec-path) - (setq bin (expand-file-name command dir)) - (when (or (and (file-executable-p bin) - (not (file-directory-p bin))) - (and (file-executable-p (setq bin (concat bin ".exe"))) - (not (file-directory-p bin)))) - (throw 'found-command bin)))))))) +;;; Harvest (defun notmuch-address-harvest-addr (result) - (let ((name-addr (plist-get result :name-addr))) - (puthash name-addr t notmuch-address-completions))) - -(defun notmuch-address-harvest-handle-result (obj) - (notmuch-address-harvest-addr obj)) + (puthash (plist-get result :name-addr) + t notmuch-address-completions)) (defun notmuch-address-harvest-filter (proc string) (when (buffer-live-p (process-buffer proc)) @@ -283,7 +269,7 @@ requiring external commands." (goto-char (point-max)) (insert string)) (notmuch-sexp-parse-partial-list - 'notmuch-address-harvest-handle-result (process-buffer proc))))) + 'notmuch-address-harvest-addr (process-buffer proc))))) (defvar notmuch-address-harvest-procs '(nil . nil) "The currently running harvests. @@ -294,7 +280,7 @@ The car is a partial harvest, and the cdr is a full harvest.") "Collect addresses completion candidates. It queries the notmuch database for messages sent/received (as -configured with `notmuch-address-command`) by the user, collects +configured with `notmuch-address-command') by the user, collects destination/source addresses from those messages and stores them in `notmuch-address-completions'. @@ -321,7 +307,7 @@ execution, CALLBACK is called when harvesting finishes." (and config-query (format " and (%s)" config-query))) from-or-to-me-query)) - (args `("address" "--format=sexp" "--format-version=4" + (args `("address" "--format=sexp" "--format-version=5" ,(if sent "--output=recipients" "--output=sender") "--deduplicate=address" ,query))) @@ -394,7 +380,7 @@ to be a saved address hash." (defun notmuch-address--save-address-hash () (when notmuch-address-save-filename (if (or (not (file-exists-p notmuch-address-save-filename)) - ;; The file exists, check it is a file we saved + ;; The file exists, check it is a file we saved. (notmuch-address--get-address-hash)) (with-temp-file notmuch-address-save-filename (let ((save-plist @@ -415,17 +401,16 @@ appear to be an address savefile. Not overwriting." (setq notmuch-address-last-harvest now) (notmuch-address-harvest nil nil - (lambda (proc event) + (lambda (_proc event) ;; If harvest fails, we want to try - ;; again when the trigger is next - ;; called + ;; again when the trigger is next called. (if (string= event "finished\n") (progn (notmuch-address--save-address-hash) (setq notmuch-address-full-harvest-finished t)) (setq notmuch-address-last-harvest 0))))))) -;; +;;; Standalone completion (defun notmuch-address-from-minibuffer (prompt) (if (not notmuch-address-command) @@ -444,7 +429,7 @@ appear to be an address savefile. Not overwriting." (let ((minibuffer-local-map rmap)) (read-string prompt))))) -;; +;;; _ (provide 'notmuch-address)