]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch-address.el
emacs docstrings: consistent indentation, newlines, periods
[notmuch] / emacs / notmuch-address.el
index b3c56cfaad9dc212b990559171c1df2499b39722..0d56fba70e2f3c14d3f8bd305165b6307ca3d79b 100644 (file)
 (declare-function company-manual-begin "company")
 
 (defvar notmuch-address-last-harvest 0
 (declare-function company-manual-begin "company")
 
 (defvar notmuch-address-last-harvest 0
-  "Time of last address harvest")
+  "Time of last address harvest.")
 
 (defvar notmuch-address-completions (make-hash-table :test 'equal)
   "Hash of email addresses for completion during email composition.
 
 (defvar notmuch-address-completions (make-hash-table :test 'equal)
   "Hash of email addresses for completion during email composition.
-  This variable is set by calling `notmuch-address-harvest'.")
+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
 
 (defvar notmuch-address-full-harvest-finished nil
   "t indicates that full completion address harvesting has been
-finished")
+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.
+
+If the hash is not present it attempts to load a saved hash."
+  (or notmuch-address-full-harvest-finished
+      (notmuch-address--load-address-hash)))
 
 (defcustom notmuch-address-command 'internal
   "Determines how address completion candidates are generated.
 
 (defcustom notmuch-address-command 'internal
   "Determines how address completion candidates are generated.
@@ -58,17 +66,18 @@ disabled."
          (const :tag "Disable address completion" nil)
          (string :tag "Use external completion command"))
   :group 'notmuch-send
          (const :tag "Disable address completion" nil)
          (string :tag "Use external completion command"))
   :group 'notmuch-send
+  :group 'notmuch-address
   :group 'notmuch-external)
 
 (defcustom notmuch-address-internal-completion '(sent nil)
   "Determines how internal address completion generates candidates.
 
 This should be a list of the form '(DIRECTION FILTER), where
   :group 'notmuch-external)
 
 (defcustom notmuch-address-internal-completion '(sent nil)
   "Determines how internal address completion generates candidates.
 
 This should be a list of the form '(DIRECTION FILTER), where
- DIRECTION is either sent or received and specifies whether the
- candidates are searched in messages sent by the user or received
- by the user (note received by is much faster), and FILTER is
- either nil or a filter-string, such as \"date:1y..\" to append
- to the query."
+DIRECTION is either sent or received and specifies whether the
+candidates are searched in messages sent by the user or received
+by the user (note received by is much faster), and FILTER is
+either nil or a filter-string, such as \"date:1y..\" to append
+to the query."
   :type '(list :tag "Use internal address completion"
               (radio
                :tag "Base completion on messages you have"
   :type '(list :tag "Use internal address completion"
               (radio
                :tag "Base completion on messages you have"
@@ -85,6 +94,19 @@ This should be a list of the form '(DIRECTION FILTER), where
         (setq notmuch-address-completions (clrhash notmuch-address-completions))
         (setq notmuch-address-full-harvest-finished nil))
   :group 'notmuch-send
         (setq notmuch-address-completions (clrhash notmuch-address-completions))
         (setq notmuch-address-full-harvest-finished nil))
   :group 'notmuch-send
+  :group 'notmuch-address
+  :group 'notmuch-external)
+
+(defcustom notmuch-address-save-filename nil
+  "Filename to save the cached completion addresses.
+
+All the addresses notmuch uses for address completion will be
+cached in this file. This has obvious privacy implications so you
+should make sure it is not somewhere publicly readable."
+  :type '(choice (const :tag "Off" nil)
+                (file :tag "Filename"))
+  :group 'notmuch-send
+  :group 'notmuch-address
   :group 'notmuch-external)
 
 (defcustom notmuch-address-selection-function 'notmuch-address-selection-function
   :group 'notmuch-external)
 
 (defcustom notmuch-address-selection-function 'notmuch-address-selection-function
@@ -96,6 +118,7 @@ See documentation of function `notmuch-address-selection-function'
 to know how address selection is made by default."
   :type 'function
   :group 'notmuch-send
 to know how address selection is made by default."
   :type 'function
   :group 'notmuch-send
+  :group 'notmuch-address
   :group 'notmuch-external)
 
 (defcustom notmuch-address-post-completion-functions nil
   :group 'notmuch-external)
 
 (defcustom notmuch-address-post-completion-functions nil
@@ -103,8 +126,7 @@ to know how address selection is made by default."
 
 The completed address is passed as an argument to each function.
 Note that this hook will be invoked for completion in headers
 
 The completed address is passed as an argument to each function.
 Note that this hook will be invoked for completion in headers
-matching `notmuch-address-completion-headers-regexp'.
-"
+matching `notmuch-address-completion-headers-regexp'."
   :type 'hook
   :group 'notmuch-address
   :group 'notmuch-hooks)
   :type 'hook
   :group 'notmuch-address
   :group 'notmuch-hooks)
@@ -124,9 +146,10 @@ matching `notmuch-address-completion-headers-regexp'.
   (message "calling notmuch-address-message-insinuate is no longer needed"))
 
 (defcustom notmuch-address-use-company t
   (message "calling notmuch-address-message-insinuate is no longer needed"))
 
 (defcustom notmuch-address-use-company t
-  "If available, use company mode for address completion"
+  "If available, use company mode for address completion."
   :type 'boolean
   :type 'boolean
-  :group 'notmuch-send)
+  :group 'notmuch-send
+  :group 'notmuch-address)
 
 (defun notmuch-address-setup ()
   (let* ((setup-company (and notmuch-address-use-company
 
 (defun notmuch-address-setup ()
   (let* ((setup-company (and notmuch-address-use-company
@@ -135,7 +158,7 @@ matching `notmuch-address-completion-headers-regexp'.
                       #'notmuch-address-expand-name)))
       (when setup-company
        (notmuch-company-setup))
                       #'notmuch-address-expand-name)))
       (when setup-company
        (notmuch-company-setup))
-      (unless (memq pair message-completion-alist)
+      (unless (member pair message-completion-alist)
        (setq message-completion-alist
              (push pair message-completion-alist)))))
 
        (setq message-completion-alist
              (push pair message-completion-alist)))))
 
@@ -170,7 +193,7 @@ elisp-based implementation or older implementation requiring
 external commands."
   (cond
    ((eq notmuch-address-command 'internal)
 external commands."
   (cond
    ((eq notmuch-address-command 'internal)
-    (when (not notmuch-address-full-harvest-finished)
+    (unless (notmuch-address--harvest-ready)
       ;; First, run quick synchronous harvest based on what the user
       ;; entered so far
       (notmuch-address-harvest original t))
       ;; First, run quick synchronous harvest based on what the user
       ;; entered so far
       (notmuch-address-harvest original t))
@@ -261,7 +284,7 @@ external commands."
 (defvar notmuch-address-harvest-procs '(nil . nil)
   "The currently running harvests.
 
 (defvar notmuch-address-harvest-procs '(nil . nil)
   "The currently running harvests.
 
-The car is a partial harvest, and the cdr is a full harvest")
+The car is a partial harvest, and the cdr is a full harvest.")
 
 (defun notmuch-address-harvest (&optional addr-prefix synchronous callback)
   "Collect addresses completion candidates.
 
 (defun notmuch-address-harvest (&optional addr-prefix synchronous callback)
   "Collect addresses completion candidates.
@@ -293,7 +316,7 @@ execution, CALLBACK is called when harvesting finishes."
                            (when config-query
                              (format " and (%s)" config-query)))
                  from-or-to-me-query))
                            (when config-query
                              (format " and (%s)" config-query)))
                  from-or-to-me-query))
-        (args `("address" "--format=sexp" "--format-version=2"
+        (args `("address" "--format=sexp" "--format-version=4"
                 ,(if sent "--output=recipients" "--output=sender")
                 "--deduplicate=address"
                 ,query)))
                 ,(if sent "--output=recipients" "--output=sender")
                 "--deduplicate=address"
                 ,query)))
@@ -323,6 +346,64 @@ execution, CALLBACK is called when harvesting finishes."
   ;; return value
   nil)
 
   ;; return value
   nil)
 
+(defvar notmuch-address--save-hash-version 1
+  "Version format of the save hash.")
+
+(defun notmuch-address--get-address-hash ()
+  "Returns the saved address hash as a plist.
+
+Returns nil if the save file does not exist, or it does not seem
+to be a saved address hash."
+  (when notmuch-address-save-filename
+    (condition-case nil
+       (with-temp-buffer
+         (insert-file-contents notmuch-address-save-filename)
+         (let ((name (read (current-buffer)))
+               (plist (read (current-buffer))))
+           ;; We do two simple sanity checks on the loaded file. We just
+           ;; check a version is specified, not that it is the current
+           ;; version, as we are allowed to over-write and a save-file with
+           ;; an older version.
+           (when (and (string= name "notmuch-address-hash")
+                      (plist-get plist :version))
+             plist)))
+      ;; The error case catches any of the reads failing.
+      (error nil))))
+
+(defun notmuch-address--load-address-hash ()
+  "Read the saved address hash and set the corresponding variables."
+  (let ((load-plist (notmuch-address--get-address-hash)))
+    (when (and load-plist
+              ;; If the user's setting have changed, or the version
+              ;; has changed, return nil to make sure the new settings
+              ;; take effect.
+              (equal (plist-get load-plist :completion-settings)
+                     notmuch-address-internal-completion)
+              (equal (plist-get load-plist :version)
+                     notmuch-address--save-hash-version))
+      (setq notmuch-address-last-harvest (plist-get load-plist :last-harvest)
+           notmuch-address-completions (plist-get load-plist :completions)
+           notmuch-address-full-harvest-finished t)
+      ;; Return t to say load was successful.
+      t)))
+
+(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
+           (notmuch-address--get-address-hash))
+       (with-temp-file notmuch-address-save-filename
+         (let ((save-plist (list :version notmuch-address--save-hash-version
+                                 :completion-settings notmuch-address-internal-completion
+                                 :last-harvest notmuch-address-last-harvest
+                                 :completions notmuch-address-completions)))
+           (print "notmuch-address-hash" (current-buffer))
+           (print save-plist (current-buffer))))
+      (message "\
+Warning: notmuch-address-save-filename %s exists but doesn't
+appear to be an address savefile.  Not overwriting."
+              notmuch-address-save-filename))))
+
 (defun notmuch-address-harvest-trigger ()
   (let ((now (float-time)))
     (when (> (- now notmuch-address-last-harvest) 86400)
 (defun notmuch-address-harvest-trigger ()
   (let ((now (float-time)))
     (when (> (- now notmuch-address-last-harvest) 86400)
@@ -333,7 +414,9 @@ execution, CALLBACK is called when harvesting finishes."
                                 ;; again when the trigger is next
                                 ;; called
                                 (if (string= event "finished\n")
                                 ;; again when the trigger is next
                                 ;; called
                                 (if (string= event "finished\n")
-                                    (setq notmuch-address-full-harvest-finished t)
+                                    (progn
+                                      (notmuch-address--save-address-hash)
+                                      (setq notmuch-address-full-harvest-finished t))
                                   (setq notmuch-address-last-harvest 0)))))))
 
 ;;
                                   (setq notmuch-address-last-harvest 0)))))))
 
 ;;