* mu4e: basic support for auto-completing contacts when composing messages

This commit is contained in:
djcb 2012-06-18 18:03:24 +03:00
parent 635e049ff7
commit 0aeb9f71c8
4 changed files with 148 additions and 10 deletions

View File

@ -71,6 +71,19 @@ sent folder."
replying to messages."
:type 'boolean
:group 'mu4e-compose)
(defcustom mu4e-compose-completion-styles '(substring)
"How to do matching for contacts-completion; see
`completion-styles'."
:type 'list
:group 'mu4e-compose)
(defcustom mu4e-compose-cycle-threshold 5
"Number of completion matches below which you can cycle through
them; see `completion-cycle-threshold'."
:type 'list
:group 'mu4e-compose)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -110,7 +123,7 @@ such all its settings apply."
(message-yank-original)
(goto-char (point-min))
(push-mark (point-max))
(funcall message-cite-function)
(funcall message-cite-function)
(pop-mark)
(buffer-string))))
@ -444,7 +457,6 @@ needed, set the Fcc header, and register the handler function."
(let ((message-hidden-headers mu4e~compose-hidden-headers))
(use-local-map mu4e-compose-mode-map)
(make-local-variable 'message-default-charset)
;; if the default charset is not set, use UTF-8
(unless message-default-charset
(setq message-default-charset 'utf-8))
@ -457,6 +469,15 @@ needed, set the Fcc header, and register the handler function."
;; mail files lives in...
(setq default-directory (expand-file-name "~/"))
;; offer completion for e-mail addresses
(when mu4e-compose-complete-addresses
(make-local-variable 'completion-styles)
(make-local-variable 'completion-cycle-threshold)
(setq
completion-cycle-threshold mu4e-compose-cycle-threshold
completion-styles mu4e-compose-completion-styles)
(add-to-list 'completion-at-point-functions 'mu4e~compose-complete-contact))
;; setup the fcc-stuff, if needed
(add-hook 'message-send-hook
(lambda ()
@ -552,7 +573,7 @@ Gnus' `message-mode'."
;; hide some headers
(let ((message-hidden-headers mu4e~compose-hidden-headers))
(message-hide-headers))
(message-hide-headers))
;; set compose mode -- so now hooks can run
(mu4e-compose-mode)
@ -560,7 +581,7 @@ Gnus' `message-mode'."
;; buffer is not user-modified yet
(mu4e~compose-set-friendly-buffer-name compose-type)
(set-buffer-modified-p nil)
;; now jump to some use positions, and start writing that mail!
(if (member compose-type '(new forward))
(message-goto-to)
@ -674,6 +695,36 @@ message."
(mu4e-compose 'new))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; address completion; inspired by org-contacts.el
(defun mu4e~compose-complete-contact (&optional start)
"Complete the text at START with a contact (ie. either 'name
<email>' or 'email')."
(interactive)
(let ((mail-abbrev-mode-regexp
(concat
"^\\(Resent-To\\|To\\|B?Cc\\|Reply-To\\|From"
"\\|Mail-Followup-To\\|Mail-Copies-To"
"\\|Disposition-Notification-To\\|Return-Receipt-To\\):"))
(eoh ;; end-of-headers
(save-excursion
(goto-char (point-min))
(search-forward-regexp mail-header-separator nil t))))
;; try to complete only when we're in the headers area,
;; looking at an address field.
(when (and (> eoh (point)) (mail-abbrev-in-expansion-header-p))
(let* ((end (point))
(start
(or start
(save-excursion
(re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
(goto-char (match-end 0))
(point))))
(orig (buffer-substring-no-properties start end))
(completion-ignore-case t))
(list start end mu4e~contacts-for-completion)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; mu4e-compose-func and mu4e-send-func are wrappers so we can set ourselves

View File

@ -213,11 +213,16 @@ The server output is as follows:
(plist-get sexp :docid)
(plist-get sexp :path)))
;; receive a pong message
;; received a pong message
((plist-get sexp :pong)
(funcall mu4e-pong-func
(plist-get sexp :version) (plist-get sexp :doccount)))
;; received a contacts message
((plist-get sexp :contacts)
(funcall mu4e-contacts-func
(plist-get sexp :contacts)))
;; something got moved/flags changed
((plist-get sexp :update)
(funcall mu4e-update-func
@ -433,6 +438,17 @@ mean:
response."
(mu4e~proc-send-command "ping"))
(defun mu4e~proc-contacts (only-personal newer-than)
"Sends the contacts command to the mu server, expecting
a (:contacts (<list>)) in response. If ONLY-PERSONAL is non-nil,
only get personal contacts, if newer-than is non-nil, get only
contacts seen after NEWER-THAN (the time_t value)."
(mu4e~proc-send-command
"contacts only-personal:%s newer-than:%d"
(if only-personal "true" "false")
(if newer-than newer-than 0)))
(defun mu4e~proc-view (docid-or-msgid &optional images)
"Get one particular message based on its DOCID-OR-MSGID (keyword
argument). Optionally, if IMAGES is non-nil, backend will any

View File

@ -43,7 +43,6 @@ recommended you use \"html2text -utf8 -width 72\"."
:group 'mu4e-view
:safe 'stringp)
(defcustom mu4e-view-prefer-html nil
"Whether to base the body display on the HTML-version of the
e-mail message (if there is any."
@ -542,6 +541,27 @@ split-window."
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; start and stopping
(defun mu4e~fill-contacts (contacts)
"We receive a list of contacts, which each contact of the form
(:name NAME :mail EMAIL)
and fill the list `mu4e~contacts-for-completion' with it, with
each element looking like
name <email>
This is used by the completion function in mu4e-compose."
(let ((lst))
(dolist (contact contacts)
(let ((name (plist-get contact :name))
(mail (plist-get contact :mail)))
;;(message "N:%S M:%S" name mail)
(when mail
(add-to-list 'lst
(if name
(format "%s <%s>" name mail)
mail)))))
(setq mu4e~contacts-for-completion lst)
(mu4e-message "Contacts received: %d"
(length mu4e~contacts-for-completion))))
(defun mu4e~check-requirements ()
"Check for the settings required for running mu4e."
@ -583,6 +603,7 @@ FUNC (if non-nil) afterwards."
;; better to check for specific features
(unless (>= emacs-major-version 23)
(error "Emacs >= 23.x is required for mu4e"))
;; set up the 'pong' handler func
(lexical-let ((func func))
(setq mu4e-pong-func
@ -597,8 +618,17 @@ FUNC (if non-nil) afterwards."
0 mu4e-update-interval 'mu4e-update-mail)))
(mu4e-message "Started mu4e with %d message%s in store"
doccount (if (= doccount 1) "" "s")))))
;; send the ping
(mu4e~proc-ping)))
;; send the ping
(mu4e~proc-ping)
;; get the address list
(when mu4e-compose-complete-addresses
(setq mu4e-contacts-func 'mu4e~fill-contacts)
(mu4e~proc-contacts
mu4e-compose-complete-only-newer-than
;; calculate time_t value -- now minus so-many days
(floor (- (float-time (current-time))
(* 3600 24 mu4e-compose-complete-only-newer-than)))))))
(defun mu4e~stop ()
"Stop the mu4e session."
@ -779,7 +809,7 @@ is ignored."
(newline)
(insert-image img imgpath nil t))))
(defun mu4e-hide-other-mu4e-buffers ()
"Bury mu4e-buffers (main, headers, view) (and delete all windows
@ -790,7 +820,7 @@ displaying it). Do _not_ bury the current buffer, though."
(lambda (win)
(with-current-buffer (window-buffer win)
(unless (eq curbuf (current-buffer))
(when (member major-mode '(mu4e-headers-mode mu4e-view-mode mu4e-main-mode))
(when (member major-mode '(mu4e-headers-mode mu4e-view-mode))
(unless (one-window-p t)
(delete-window win)))))) nil t)))

View File

@ -112,6 +112,39 @@ else: don't split (show either headers or messages, not both) Also
see `mu4e-headers-visible-lines' and
`mu4e-headers-visible-columns'.")
;; completion; we put them here rather than in mu4e-compose, as mu4e-utils needs
;; the variables.
(defgroup mu4e-compose nil
"Message-composition related settings."
:group 'mu4e)
;; address completion
(defcustom mu4e-compose-complete-addresses t
"Whether to do auto-completion of e-mail addresses."
:type 'boolean
:group 'mu4e-compose)
(defcustom mu4e-compose-complete-only-personal t
"Whether to consider only 'personal' e-mail addresses,
i.e. addresses from messages where user was explicitly in one of
the address fields (this excludes mailing list messages)."
:type 'boolean
:group 'mu4e-compose)
(defcustom mu4e-compose-complete-only-newer-than 500
"Consider only contacts last seen less than so many *days*. This
excludes really old contacts. Set to nil to not have any time-based
restriction."
:type 'integer
:group 'mu4e-compose)
(defcustom mu4e-compose-my-email-addresses `(,user-mail-address)
"List of e-mail addresses to consider 'my email addresses',
ie. addresses whose presence in an email imply that it is a
personal message."
:type '(string)
:group 'mu4e-compose)
;; Folders
(defgroup mu4e-folders nil
@ -368,6 +401,10 @@ view). Most fields should be self-explanatory. A special one is
(defvar mu4e~view-msg nil "The message being viewed in view mode.")
(defvar mu4e~contacts-for-completion nil
"List of contacts (ie. 'name <e-mail>'),
used by the completion functions in mu4e-compose, and filled when
mu4e starts.")
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -433,6 +470,10 @@ the server process.")
"A function called for each (:pong type ....) sexp received from
the server process.")
(defvar mu4e-contacts-func 'mu4e~default-handler
"A function called for each (:contacts (<list-of-contacts>) sexp
received from the server process.")
(defvar mu4e-temp-func 'mu4e~default-handler
"A function called for each (:temp <file> <cookie>) sexp received
from the server process.")