mirror of https://github.com/djcb/mu.git
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:
parent
f17995b113
commit
8ad5fd49c9
|
@ -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 "")
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
15
mu4e/mu4e.el
15
mu4e/mu4e.el
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue