emacs: address: save hash

This allows the user to save the address hash so that it is much
faster for the first completion after a restart. This defaults to off
as there are privacy implications to saving this information.

The code tries hard to avoid overwriting the wrong file. It also notes
if changes have been made to any of the relevant user settings, so
that the user does not get surprising results (i.e., outdated options
being used). Finally it stores some version information so that is
easy for us to update the format of the save file.
This commit is contained in:
Mark Walters 2016-11-20 16:50:34 +00:00 committed by David Bremner
parent df9736f20b
commit 08343d3da0

View file

@ -37,11 +37,15 @@
(defvar notmuch-address-full-harvest-finished nil (defvar notmuch-address-full-harvest-finished nil
"t indicates that full completion address harvesting has been "t indicates that full completion address harvesting has been
finished. Use notmuch-address--harvest-ready to access.") finished. Use notmuch-address--harvest-ready to access as that
will load a saved hash if necessary (and available).")
(defun notmuch-address--harvest-ready () (defun notmuch-address--harvest-ready ()
"Return t if there is a full address hash available." "Return t if there is a full address hash available.
notmuch-address-full-harvest-finished)
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 (defcustom notmuch-address-command 'internal
"Determines how address completion candidates are generated. "Determines how address completion candidates are generated.
@ -91,6 +95,17 @@ This should be a list of the form '(DIRECTION FILTER), where
:group 'notmuch-send :group 'notmuch-send
:group 'notmuch-external) :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-external)
(defcustom notmuch-address-selection-function 'notmuch-address-selection-function (defcustom notmuch-address-selection-function 'notmuch-address-selection-function
"The function to select address from given list. The function is "The function to select address from given list. The function is
called with PROMPT, COLLECTION, and INITIAL-INPUT as arguments called with PROMPT, COLLECTION, and INITIAL-INPUT as arguments
@ -327,6 +342,64 @@ execution, CALLBACK is called when harvesting finishes."
;; return value ;; return value
nil) 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 () (defun notmuch-address-harvest-trigger ()
(let ((now (float-time))) (let ((now (float-time)))
(when (> (- now notmuch-address-last-harvest) 86400) (when (> (- now notmuch-address-last-harvest) 86400)
@ -337,7 +410,9 @@ execution, CALLBACK is called when harvesting finishes."
;; again when the trigger is next ;; again when the trigger is next
;; called ;; called
(if (string= event "finished\n") (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)))))))
;; ;;