mu4e: contacts: use mu4e--contacts-set

We use to have mu4e--contacts-hash, with name mapping to rank; that was
needlessly complicated since most completion engine sort alphabetically, making
the rank totally irrelevant (in practice, it doesn't matter much from the
end-user pov).

Anyway, simplify that part, maintain a set of contacts without any rank (which
what the server delivers now).

Also update the _default_ mu4e-contact-process-function to filter out anything
with 'reply' since it's not very useful for autocompletion.
This commit is contained in:
Dirk-Jan C. Binnema 2022-05-09 21:22:17 +03:00
parent 81689f0af3
commit db86e7b5ee
4 changed files with 35 additions and 51 deletions

View File

@ -224,9 +224,9 @@ Message-ID."
"Complete address STR with predication PRED for ACTION." "Complete address STR with predication PRED for ACTION."
(cond (cond
((eq action nil) ((eq action nil)
(try-completion str mu4e--contacts-hash pred)) (try-completion str mu4e--contacts-set pred))
((eq action t) ((eq action t)
(all-completions str mu4e--contacts-hash pred)) (all-completions str mu4e--contacts-set pred))
((eq action 'metadata) ((eq action 'metadata)
;; our contacts are already sorted - just need to tell the ;; our contacts are already sorted - just need to tell the
;; completion machinery not to try to undo that... ;; completion machinery not to try to undo that...
@ -357,7 +357,7 @@ buffers; lets remap its faces so it uses the ones for mu4e."
(mu4e~compose-register-message-save-hooks) (mu4e~compose-register-message-save-hooks)
;; offer completion for e-mail addresses ;; offer completion for e-mail addresses
(when mu4e-compose-complete-addresses (when mu4e-compose-complete-addresses
(unless mu4e--contacts-hash (unless mu4e--contacts-set
;; work-around for https://github.com/djcb/mu/issues/1016 ;; work-around for https://github.com/djcb/mu/issues/1016
(mu4e--request-contacts-maybe)) (mu4e--request-contacts-maybe))
(mu4e~compose-setup-completion)) (mu4e~compose-setup-completion))

View File

@ -87,14 +87,18 @@ their canonical counterpart; useful as an example."
"mu4e 1.3.2") "mu4e 1.3.2")
(defcustom mu4e-contact-process-function (defcustom mu4e-contact-process-function
(lambda(addr) ;; filter-out no-reply addresses (lambda(addr)
(unless (string-match-p "no[t]?[-\\.]?repl\\(y\\|ies\\)" addr) (cond
addr)) ((string-match-p "reply" addr)
;; no-reply adresses are not useful of course, but neither are are
;; reply-xxxx addresses since they're autogenerated only useful for direct
;; replies.
nil)
(t addr)))
"Function for processing contact information for use in auto-completion. "Function for processing contact information for use in auto-completion.
The function receives the contact as a string, e.g The function receives the contact as a string, e.g \"Foo Bar
\"Foo Bar <foo.bar@example.com>\" <foo.bar@example.com>\" \"cuux@example.com\"
\"cuux@example.com\"
The function should return either: The function should return either:
- nil: do not use this contact for completion - nil: do not use this contact for completion
@ -121,11 +125,8 @@ predicate function. A value of nil keeps all the addresses."
(defvar mu4e--contacts-tstamp "0" (defvar mu4e--contacts-tstamp "0"
"Timestamp for the most recent contacts update." ) "Timestamp for the most recent contacts update." )
(defvar mu4e--contacts-hash nil (defvar mu4e--contacts-set nil
"Hash that maps contacts (ie. 'name <e-mail>') to an integer for sorting. "Set with the full contact addresses for autocompletion.")
We need to keep this information around to quickly re-sort
subsets of the contacts in the completions function in
mu4e-compose.")
;;; user mail address ;;; user mail address
(defun mu4e-personal-addresses(&optional no-regexp) (defun mu4e-personal-addresses(&optional no-regexp)
@ -236,36 +237,24 @@ case a phrase contains a quote, it will be escaped."
(defun mu4e--update-contacts (contacts &optional tstamp) (defun mu4e--update-contacts (contacts &optional tstamp)
"Receive a sorted list of CONTACTS newer than TSTAMP. "Receive a sorted list of CONTACTS newer than TSTAMP.
Each of the contacts has the form Update an internal set with it.
(FULL_EMAIL_ADDRESS . RANK) and fill `mu4e--contacts-hash' with
it, with each contact mapped to an integer for their ranking.
This is used by the completion function in mu4e-compose." This is used by the completion function in mu4e-compose."
;; We have our nicely sorted list, map them to a list
;; of increasing integers. We use that map in the composer
;; to sort them there. It would have been so much easier if emacs
;; allowed us to use the sorted-list as-is, but no such luck.
(let ((n 0)) (let ((n 0))
(unless mu4e--contacts-hash (unless mu4e--contacts-set
(setq mu4e--contacts-hash (make-hash-table :test 'equal :weakness nil (setq mu4e--contacts-set (make-hash-table :test 'equal :weakness nil
:size (length contacts)))) :size (length contacts))))
(dolist (contact contacts) (dolist (contact contacts)
(cl-incf n) (cl-incf n)
(let* ((address (plist-get contact :address)) (when (functionp mu4e-contact-process-function)
(address (setq contact (funcall mu4e-contact-process-function contact)))
(if (functionp mu4e-contact-process-function) (when contact ;; note the explicit deccode; the strings we get are
(funcall mu4e-contact-process-function address)
address)))
(when address ;; note the explicit deccode; the strings we get are
;; utf-8, but emacs doesn't know yet. ;; utf-8, but emacs doesn't know yet.
(puthash (decode-coding-string address 'utf-8) (puthash (decode-coding-string contact 'utf-8) t mu4e--contacts-set)))
(plist-get contact :rank) mu4e--contacts-hash))))
(setq mu4e--contacts-tstamp (or tstamp "0")) (setq mu4e--contacts-tstamp (or tstamp "0"))
(unless (zerop n) (unless (zerop n)
(mu4e-index-message "Contacts updated: %d; total %d" (mu4e-index-message "Contacts updated: %d; total %d"
n (hash-table-count mu4e--contacts-hash))))) n (hash-table-count mu4e--contacts-set)))))
(defun mu4e-contacts-info () (defun mu4e-contacts-info ()
"Display information about the contacts-cache. "Display information about the contacts-cache.
@ -274,31 +263,26 @@ For testing/debugging."
(with-current-buffer (get-buffer-create "*mu4e-contacts-info*") (with-current-buffer (get-buffer-create "*mu4e-contacts-info*")
(erase-buffer) (erase-buffer)
(insert (format "complete addresses: %s\n" (insert (format "complete addresses: %s\n"
(if mu4e-compose-complete-addresses "yes" "no"))) (if mu4e-compose-complete-addresses "yes" "no")))
(insert (format "only personal addresses: %s\n" (insert (format "only personal addresses: %s\n"
(if mu4e-compose-complete-only-personal "yes" "no"))) (if mu4e-compose-complete-only-personal "yes" "no")))
(insert (format "only addresses seen after: %s\n" (insert (format "only addresses seen after: %s\n"
(or mu4e-compose-complete-only-after "no restrictions"))) (or mu4e-compose-complete-only-after "no restrictions")))
(when mu4e--contacts-hash (when mu4e--contacts-set
(insert (format "number of contacts cached: %d\n\n" (insert (format "number of contacts cached: %d\n\n"
(hash-table-count mu4e--contacts-hash))) (hash-table-count mu4e--contacts-set)))
(let ((contacts)) (maphash (lambda (contact _)
(maphash (lambda (addr rank) (insert (format "%s\n" contact))) mu4e--contacts-set))
(setq contacts (cons (cons rank addr) contacts)))
mu4e--contacts-hash)
(setq contacts (sort contacts
(lambda(cell1 cell2) (< (car cell1) (car cell2)))))
(dolist (contact contacts)
(insert (format "%s\n" (cdr contact))))))
(pop-to-buffer "*mu4e-contacts-info*"))) (pop-to-buffer "*mu4e-contacts-info*")))
(declare-function mu4e--server-contacts "mu4e-server") (declare-function mu4e--server-contacts "mu4e-server")
(defun mu4e--request-contacts-maybe () (defun mu4e--request-contacts-maybe ()
"If `mu4e-compose-complete-addresses' is non-nil, get/update "Maybe update the set of contacts for autocompletion.
the list of contacts we use for autocompletion; otherwise, do
If `mu4e-compose-complete-addresses' is non-nil, get/update the
list of contacts we use for autocompletion; otherwise, do
nothing." nothing."
(when mu4e-compose-complete-addresses (when mu4e-compose-complete-addresses
(mu4e--server-contacts (mu4e--server-contacts

View File

@ -441,7 +441,7 @@ status, STATUS."
((looking-back "\\(from\\|to\\|cc\\|bcc\\|contact\\|recip\\):\\([a-zA-Z0-9/.@]*\\)" nil) ((looking-back "\\(from\\|to\\|cc\\|bcc\\|contact\\|recip\\):\\([a-zA-Z0-9/.@]*\\)" nil)
(list (match-beginning 2) (list (match-beginning 2)
(match-end 2) (match-end 2)
mu4e--contacts-hash mu4e--contacts-set
:exit-function :exit-function
#'mu4e--search-completion-contacts-action)) #'mu4e--search-completion-contacts-action))
((looking-back "list:\\([a-zA-Z0-9/.@]*\\)" nil) ((looking-back "list:\\([a-zA-Z0-9/.@]*\\)" nil)

View File

@ -151,7 +151,7 @@ invoke
(mu4e--maildirs-with-query))))) (mu4e--maildirs-with-query)))))
;; maybe request the list of contacts, automatically refreshed after ;; maybe request the list of contacts, automatically refreshed after
;; reindexing ;; reindexing
(unless mu4e--contacts-hash (mu4e--request-contacts-maybe))) (unless mu4e--contacts-set (mu4e--request-contacts-maybe)))
(defun mu4e--stop () (defun mu4e--stop ()
"Stop mu4e." "Stop mu4e."
@ -251,7 +251,7 @@ chance."
"Clear any cached resources." "Clear any cached resources."
(setq (setq
mu4e-maildir-list nil mu4e-maildir-list nil
mu4e--contacts-hash nil mu4e--contacts-set nil
mu4e--contacts-tstamp "0")) mu4e--contacts-tstamp "0"))
;;; _ ;;; _
(provide 'mu4e) (provide 'mu4e)