mu4e: support batched query results

Support the new batched query results from the mu server; these are much faster
to render (2-3x it seems).

Rearrange the code a bit to avoid byte-compiler warnings.

Obsolete mu4e-header-func, to be replaced by mu4e-headers-append-func.
This commit is contained in:
Dirk-Jan C. Binnema 2021-10-21 19:21:09 +03:00
parent f17995b113
commit 8ad5fd49c9
4 changed files with 155 additions and 156 deletions

View File

@ -318,93 +318,10 @@ This is mostly useful for profiling.")
(goto-char (point-min))
(insert (propertize msg 'face 'mu4e-system-face 'intangible t)))))))
;;; Handler functions
;; next are a bunch of handler functions; those will be called from mu4e~proc in
;; response to output from the server process
(defun mu4e~headers-view-handler (msg)
"Handler function for displaying a message."
(mu4e-view msg))
(defun mu4e~headers-view-this-message-p (docid)
"Is DOCID currently being viewed?"
(when (buffer-live-p (mu4e-get-view-buffer))
(with-current-buffer (mu4e-get-view-buffer)
(eq docid (plist-get mu4e~view-message :docid)))))
(defun mu4e~headers-update-handler (msg is-move maybe-view)
"Update handler, will be called when a message has been updated
in the database. This function will update the current list of
headers."
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let* ((docid (mu4e-message-field msg :docid))
(initial-message-at-point (mu4e~headers-docid-at-point))
(initial-column (current-column))
(point (mu4e~headers-docid-pos docid))
(markinfo (gethash docid mu4e~mark-map)))
(when point ;; is the message present in this list?
;; if it's marked, unmark it now
(when (mu4e-mark-docid-marked-p docid)
(mu4e-mark-set 'unmark))
;; re-use the thread info from the old one; this is needed because
;; *update* messages don't have thread info by themselves (unlike
;; search results)
;; since we still have the search results, re-use
;; those
(plist-put msg :meta
(mu4e~headers-field-for-docid docid :meta))
;; first, remove the old one (otherwise, we'd have two headers with
;; the same docid...
(mu4e~headers-remove-header docid t)
;; if we're actually viewing this message (in mu4e-view mode), we
;; update it; that way, the flags can be updated, as well as the path
;; (which is useful for viewing the raw message)
(when (and maybe-view (mu4e~headers-view-this-message-p docid))
(mu4e-view msg))
;; now, if this update was about *moving* a message, we don't show it
;; anymore (of course, we cannot be sure if the message really no
;; longer matches the query, but this seem a good heuristic. if it
;; was only a flag-change, show the message with its updated flags.
(unless is-move
(mu4e~headers-header-handler msg point))
;; restore the mark, if any. See #2076.
(when (and markinfo (mu4e~headers-goto-docid docid))
(mu4e-mark-at-point (car markinfo) (cdr markinfo)))
(if (and initial-message-at-point
(mu4e~headers-goto-docid initial-message-at-point))
(progn
(move-to-column initial-column)
(mu4e~headers-highlight initial-message-at-point))
;; attempt to highlight the corresponding line and make it visible
(mu4e~headers-highlight docid))
(run-hooks 'mu4e-message-changed-hook))))))
(defun mu4e~headers-remove-handler (docid &optional skip-hook)
"Remove handler, will be called when a message with DOCID has
been removed from the database. This function will hide the removed
message from the current list of headers. If the message is not
present, don't do anything.
If SKIP-HOOK is absent or nil, `mu4e-message-changed-hook' will be invoked."
(when (buffer-live-p (mu4e-get-headers-buffer))
(mu4e~headers-remove-header docid t))
;; if we were viewing this message, close it now.
(when (and (mu4e~headers-view-this-message-p docid)
(buffer-live-p (mu4e-get-view-buffer)))
(unless (eq mu4e-split-view 'single-window)
(mapc #'delete-window (get-buffer-window-list
(mu4e-get-view-buffer) nil t)))
(kill-buffer (mu4e-get-view-buffer)))
(unless skip-hook
(run-hooks 'mu4e-message-changed-hook)))
;;; Misc
@ -464,6 +381,7 @@ nil. Returns the docid, or nil if there is none."
(get-text-property (line-beginning-position) 'docid)))
(defun mu4e~headers-goto-docid (docid &optional to-mark)
"Go to the beginning of the line with the header with docid
DOCID, or nil if it cannot be found. If the optional TO-MARK is
@ -669,15 +587,12 @@ found."
(:size (mu4e-display-size val))
(t (mu4e~headers-custom-field-value msg field)))))
(defun mu4e~headers-truncate-field-fast (val width)
"Truncate VAL to WIDTH. Fast and somewhat inaccurate."
(if width
(truncate-string-to-width val width 0 ?\s truncate-string-ellipsis)
val))
(defun mu4e~headers-truncate-field-precise (field val width)
"Return VAL truncated to one less than WIDTH, with a trailing
space propertized with a 'display text property which expands to
@ -742,16 +657,131 @@ displaying in the header view."
(mapconcat (lambda (f-w) (mu4e~headers-field-handler f-w msg))
mu4e-headers-fields " "))))
(defsubst mu4e~headers-insert-header (msg pos)
"Insert a header for MSG at point POS."
(when-let ((line (mu4e~message-header-line msg))
(docid (plist-get msg :docid)))
(goto-char pos)
(insert
(propertize
(concat
(mu4e~headers-docid-cookie docid)
mu4e~mark-fringe line "\n")
'docid docid 'msg msg))))
(defun mu4e~headers-remove-header (docid &optional ignore-missing)
"Remove header with DOCID at point.
When IGNORE-MISSING is non-nill, don't raise an error when the
docid is not found."
(with-current-buffer (mu4e-get-headers-buffer)
(if (mu4e~headers-goto-docid docid)
(let ((inhibit-read-only t))
(delete-region (line-beginning-position) (line-beginning-position 2)))
(unless ignore-missing
(mu4e-error "Cannot find message with docid %S" docid)))))
;;; Handler functions
;; next are a bunch of handler functions; those will be called from mu4e~proc in
;; response to output from the server process
(defun mu4e~headers-view-handler (msg)
"Handler function for displaying a message."
(mu4e-view msg))
(defun mu4e~headers-view-this-message-p (docid)
"Is DOCID currently being viewed?"
(when (buffer-live-p (mu4e-get-view-buffer))
(with-current-buffer (mu4e-get-view-buffer)
(eq docid (plist-get mu4e~view-message :docid)))))
;; note: this function is very performance-sensitive
(defun mu4e~headers-header-handler (msg &optional point)
"Create a one line description of MSG in this buffer, at POINT,
if provided, or at the end of the buffer otherwise."
(defun mu4e~headers-append-handler (msglst)
"Append one-line descriptions of messages in MSGLIST.
Do this at the end of the headers-buffer."
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let ((line (mu4e~message-header-line msg)))
(when line
(mu4e~headers-add-header line (mu4e-message-field msg :docid)
point msg))))))
(save-excursion
(let ((inhibit-read-only t))
(seq-do
(lambda (msg)
(mu4e~headers-insert-header msg (point-max)))
msglst))))))
(defun mu4e~headers-update-handler (msg is-move maybe-view)
"Update handler, will be called when a message has been updated
in the database. This function will update the current list of
headers."
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let* ((docid (mu4e-message-field msg :docid))
(initial-message-at-point (mu4e~headers-docid-at-point))
(initial-column (current-column))
(inhibit-read-only t)
(point (mu4e~headers-docid-pos docid))
(markinfo (gethash docid mu4e~mark-map)))
(when point ;; is the message present in this list?
;; if it's marked, unmark it now
(when (mu4e-mark-docid-marked-p docid)
(mu4e-mark-set 'unmark))
;; re-use the thread info from the old one; this is needed because
;; *update* messages don't have thread info by themselves (unlike
;; search results)
;; since we still have the search results, re-use
;; those
(plist-put msg :meta
(mu4e~headers-field-for-docid docid :meta))
;; first, remove the old one (otherwise, we'd have two headers with
;; the same docid...
(mu4e~headers-remove-header docid t)
;; if we're actually viewing this message (in mu4e-view mode), we
;; update it; that way, the flags can be updated, as well as the path
;; (which is useful for viewing the raw message)
(when (and maybe-view (mu4e~headers-view-this-message-p docid))
(mu4e-view msg))
;; now, if this update was about *moving* a message, we don't show it
;; anymore (of course, we cannot be sure if the message really no
;; longer matches the query, but this seem a good heuristic. if it
;; was only a flag-change, show the message with its updated flags.
(unless is-move
(save-excursion
(mu4e~headers-insert-header msg point)))
;; restore the mark, if any. See #2076.
(when (and markinfo (mu4e~headers-goto-docid docid))
(mu4e-mark-at-point (car markinfo) (cdr markinfo)))
(if (and initial-message-at-point
(mu4e~headers-goto-docid initial-message-at-point))
(progn
(move-to-column initial-column)
(mu4e~headers-highlight initial-message-at-point))
;; attempt to highlight the corresponding line and make it visible
(mu4e~headers-highlight docid))
(run-hooks 'mu4e-message-changed-hook))))))
(defun mu4e~headers-remove-handler (docid)
"Remove handler, will be called when a message with DOCID has
been removed from the database. This function will hide the removed
message from the current list of headers. If the message is not
present, don't do anything."
(when (buffer-live-p (mu4e-get-headers-buffer))
(mu4e~headers-remove-header docid t))
;; if we were viewing this message, close it now.
(when (and (mu4e~headers-view-this-message-p docid)
(buffer-live-p (mu4e-get-view-buffer)))
(unless (eq mu4e-split-view 'single-window)
(mapc #'delete-window (get-buffer-window-list
(mu4e-get-view-buffer) nil t)))
(kill-buffer (mu4e-get-view-buffer))))
;;; Performing queries (internal)
@ -1087,7 +1117,7 @@ after the end of the search results."
(defun mu4e~headers-maybe-auto-update ()
"Update the current headers buffer after indexing has brought
some changes, `mu4e-headers-auto-update' is non-nil and there
isno user-interaction ongoing."
is no user-interaction ongoing."
(when (and mu4e-headers-auto-update ;; must be set
mu4e-index-update-status
(> 0 (plist-get mu4e-index-update-status :updated))
@ -1183,33 +1213,6 @@ message plist, or nil if not found."
'msg msg)))
(goto-char oldpoint))))
(defun mu4e~headers-add-header (str docid point &optional msg)
"Add header STR with DOCID to the buffer at POINT if non-nil, or
at (point-max) otherwise. If MSG is not nil, add it as the
text-property `msg'."
(when (buffer-live-p (mu4e-get-headers-buffer))
(with-current-buffer (mu4e-get-headers-buffer)
(let ((inhibit-read-only t))
(save-excursion
(goto-char (if point point (point-max)))
(insert
(propertize
(concat
(mu4e~headers-docid-cookie docid)
mu4e~mark-fringe
str "\n")
'docid docid 'msg msg)))))))
(defun mu4e~headers-remove-header (docid &optional ignore-missing)
"Remove header with DOCID at point.
When IGNORE-MISSING is non-nill, don't raise an error when the
docid is not found."
(with-current-buffer (mu4e-get-headers-buffer)
(if (mu4e~headers-goto-docid docid)
(let ((inhibit-read-only t))
(delete-region (line-beginning-position) (line-beginning-position 2)))
(unless ignore-missing
(mu4e-error "Cannot find message with docid %S" docid)))))
;;; Queries & searching
(defvar mu4e~headers-mode-line-label "")

View File

@ -39,7 +39,6 @@
"Search-related settings."
:group 'mu4e)
(define-obsolete-variable-alias 'mu4e-headers-results-limit
'mu4e-search-results-limit "1.7.0")
(defcustom mu4e-search-results-limit 500
@ -130,8 +129,12 @@ but also manually invoked searches."
"Maximum size for the query stacks.")
(defvar mu4e--search-last-query nil
"The present (most recent) query.")
;;; Interactive functions
(declare-function mu4e--search-execute "mu4e-headers")
(defun mu4e-search (&optional expr prompt edit ignore-history msgid show)
"Search for query EXPR.

View File

@ -98,10 +98,12 @@ passed the docid and the draft-path of the sent message.")
The function is passed a message sexp as argument. See
`mu4e--server-filter' for the format.")
(defvar mu4e-header-func nil
"Function called for each message-header received.
The function is passed a msg plist as argument. See
`mu4e--server-filter' for the format.")
(make-obsolete-variable 'mu4e-header-func "mu4e-headers-append-func" "1.7.4")
(defvar mu4e-headers-append-func nil
"Function called with a list of headers to append.
The function is passed a list of message plists as argument. See
See `mu4e--server-filter' for the details.")
(defvar mu4e-found-func nil
"Function called for when we received a :found sexp.
@ -191,17 +193,6 @@ removed."
(setq mu4e--server-buf (substring mu4e--server-buf sexp-len))
(car objcons)))))))
(defsubst mu4e--message (msgdata)
"Convert MSGDATA into a msg plist.
This receives a 'message-data' blob of the form
(:meta (...) :message (...))
and turns it into (:message :meta (...) ... ).
The former version is what the server is optimized for,
but the latter is what the header wants."
(plist-put (plist-get msgdata :message) :meta (plist-get msgdata :meta)))
(defun mu4e--server-filter (_proc str)
"Filter string STR from PROC.
This processes the 'mu server' output. It accumulates the
@ -216,9 +207,8 @@ The server output is as follows:
=> passed to `mu4e-error-func'.
2a. a header exp looks something like:
(:header
:meta (....)
:message (
(:headers
( ;; message 1
:docid 1585
:from ((\"Donald Duck\" . \"donald@example.com\"))
:to ((\"Mickey Mouse\" . \"mickey@example.com\"))
@ -231,9 +221,13 @@ The server output is as follows:
:maildir: \"/archive\"
:path \"/home/mickey/Maildir/inbox/cur/1312_3.32282.pluto,4cd5bd4e9:2,\"
:priority high
:flags (new unread)))
:flags (new unread)
:meta <meta-data>
)
( .... more messages )
)
;; eox
=> this will be passed to `mu4e-header-func'.
=> this will be passed to `mu4e-headers-append-func'.
2b. After the list of headers has been returned (see 2a.),
we'll receive a sexp that looks like
@ -243,7 +237,7 @@ The server output is as follows:
3. a view looks like:
(:view <msg-sexp>)
=> the <msg-sexp> (see 2.) will be passed to `mu4e-view-func'.
like :header, but the :message also contains :body-txt and/or :body-html
the <msg-sexp> also contains :body-txt and/or :body-html
4. a database update looks like:
(:update <msg-sexp> :move <nil-or-t>)
@ -268,8 +262,8 @@ The server output is as follows:
(mu4e-log 'from-server "%S" sexp)
(cond
;; a header plist can be recognized by the existence of a :date field
((plist-get sexp :header)
(funcall mu4e-header-func (mu4e--message (plist-get sexp :header))))
((plist-get sexp :headers)
(funcall mu4e-headers-append-func (plist-get sexp :headers)))
;; the found sexp, we receive after getting all the headers
((plist-get sexp :found)
@ -277,7 +271,7 @@ The server output is as follows:
;; viewing a specific message
((plist-get sexp :view)
(funcall mu4e-view-func (mu4e--message (plist-get sexp :view))))
(funcall mu4e-view-func (plist-get sexp :view)))
;; receive an erase message
((plist-get sexp :erase)
@ -304,7 +298,7 @@ The server output is as follows:
;; something got moved/flags changed
((plist-get sexp :update)
(funcall mu4e-update-func
(mu4e--message (plist-get sexp :update))
(plist-get sexp :update)
(plist-get sexp :move)
(plist-get sexp :maybe-view)))
@ -316,7 +310,7 @@ The server output is as follows:
((plist-get sexp :compose)
(funcall mu4e-compose-func
(plist-get sexp :compose)
(mu4e--message (plist-get sexp :original))
(plist-get sexp :original)
(plist-get sexp :include)))
;; get some info

View File

@ -64,7 +64,6 @@
;; desktop-save-mode; so let's turn that off.
(with-eval-after-load 'desktop
(eval '(add-to-list 'desktop-modes-not-to-save 'mu4e-compose-mode)))
;;;###autoload
(defun mu4e (&optional background)
@ -232,13 +231,13 @@ successful, call FUNC (if non-nil) afterwards."
"Initialize the server message handlers.
Only set set them if they were nil before, so overriding has a
chance."
(mu4e-setq-if-nil mu4e-error-func #'mu4e--error-handler)
(mu4e-setq-if-nil mu4e-update-func #'mu4e~headers-update-handler)
(mu4e-setq-if-nil mu4e-remove-func #'mu4e~headers-remove-handler)
(mu4e-setq-if-nil mu4e-view-func #'mu4e~headers-view-handler)
(mu4e-setq-if-nil mu4e-header-func #'mu4e~headers-header-handler)
(mu4e-setq-if-nil mu4e-found-func #'mu4e~headers-found-handler)
(mu4e-setq-if-nil mu4e-erase-func #'mu4e~headers-clear)
(mu4e-setq-if-nil mu4e-error-func #'mu4e--error-handler)
(mu4e-setq-if-nil mu4e-update-func #'mu4e~headers-update-handler)
(mu4e-setq-if-nil mu4e-remove-func #'mu4e~headers-remove-handler)
(mu4e-setq-if-nil mu4e-view-func #'mu4e~headers-view-handler)
(mu4e-setq-if-nil mu4e-headers-append-func #'mu4e~headers-append-handler)
(mu4e-setq-if-nil mu4e-found-func #'mu4e~headers-found-handler)
(mu4e-setq-if-nil mu4e-erase-func #'mu4e~headers-clear)
(mu4e-setq-if-nil mu4e-sent-func #'mu4e--default-handler)
(mu4e-setq-if-nil mu4e-compose-func #'mu4e~compose-handler)