mu4e: use caching for modeline items

Avoid excessive (unnecessary) recalculation.
This commit is contained in:
Dirk-Jan C. Binnema 2023-01-08 13:39:42 +02:00
parent f77bc903e7
commit 5e5a74ed22
6 changed files with 105 additions and 97 deletions

View File

@ -167,11 +167,11 @@ the latest query-results.")
(defun mu4e--reset-baseline () (defun mu4e--reset-baseline ()
(setq mu4e--baseline (mu4e-server-query-results) (setq mu4e--baseline (mu4e-server-query-results)
mu4e--baseline-tstamp (current-time)) mu4e--baseline-tstamp (current-time))
(mu4e-last-query-results 'force)) ; for side-effects (mu4e-last-query-results 'refresh)) ; for side-effects
(defvar mu4e--last-query-results-cached nil) (defvar mu4e--last-query-results-cached nil)
(defun mu4e-last-query-results(&optional force) (defun mu4e-last-query-results(&optional refresh)
"Get the results (counts) of the latest queries. "Get the results (counts) of the latest queries.
Either read form the cache or update them when oudated or FORCE Either read form the cache or update them when oudated or FORCE
@ -195,32 +195,32 @@ The results are a list of elements of the form
baseline part is optional (see `mu4e-reset-query-results') for baseline part is optional (see `mu4e-reset-query-results') for
more details). more details).
Uses a cached string unless its nil or FORCE is non-nil." Uses a cached string unless it is nil or REFRESH is non-nil."
(if (and mu4e--last-query-results-cached (not force)) (or (and (not refresh) mu4e--last-query-results-cached)
mu4e--last-query-results-cached ;; use cache (setq mu4e--last-query-results-cached
;; otherwise, recalculate. (let* ((favorite (mu4e-favorite-bookmark))
(let* ((favorite (mu4e-favorite-bookmark)) (favorite-query
(favorite-query (and favorite (mu4e--bookmark-query favorite))))
(and favorite (mu4e--bookmark-query favorite)))) ;; walk over the remembered queries
(setq mu4e--last-query-results-cached ;; walk over the remembered queries ;; and augment them with the baseline data and ':favorite' flag, if
;; and augment them with the baseline data and ':favorite' flag, if ;; any.
;; any. (seq-map
(seq-map (lambda (qres)
(lambda (qres) ;; note: queries can be _functions_ too; use their
;; note: queries can be _functions_ too; use their ;; string value.
;; string value. (let* ((query (mu4e--bookmark-query qres))
(let* ((query (mu4e--bookmark-query qres)) (bres (seq-find ;; find the corresponding baseline entry
(bres (seq-find ;; find the corresponding baseline entry (lambda (bq)
(lambda (bq) (string= query (mu4e--bookmark-query bq)))
(string= query (mu4e--bookmark-query bq))) mu4e--baseline)))
mu4e--baseline))) (when (string= query (or favorite-query ""))
(when (string= query (or favorite-query "")) (plist-put qres :favorite t))
(plist-put qres :favorite t)) (when bres
(when bres (plist-put qres :baseline
(plist-put qres :baseline `(:count ,(plist-get bres :count)
`(:count ,(plist-get bres :count) :unread ,(plist-get bres :unread))))
:unread ,(plist-get bres :unread)))) qres))
qres)) (mu4e-server-query-results)))))) (mu4e-server-query-results))))))
(defun mu4e-last-query-result (query) (defun mu4e-last-query-result (query)
"Get the last result for some QUERY or nil if not found. "Get the last result for some QUERY or nil if not found.
@ -247,51 +247,39 @@ I.e., very new messages.")
(when-let ((fav (mu4e--bookmark-query (mu4e-favorite-bookmark)))) (when-let ((fav (mu4e--bookmark-query (mu4e-favorite-bookmark))))
(mu4e-search-bookmark fav))) (mu4e-search-bookmark fav)))
(defvar mu4e--bookmarks-modeline-cached nil)
(defun mu4e--bookmarks-modeline-item () (defun mu4e--bookmarks-modeline-item ()
"Modeline item showing message counts for the favorite bookmark. "Modeline item showing message counts for the favorite bookmark.
This uses the one special ':favorite' bookmark, and if there is This uses the one special ':favorite' bookmark, and if there is
one, creates a propertized string for display in the modeline." one, creates a propertized string for display in the modeline."
(or mu4e--bookmarks-modeline-cached (when-let ((fav ;; any results for the favorite bookmark item?
(setq mu4e--bookmarks-modeline-cached (seq-find (lambda (bm) (plist-get bm :favorite))
(when-let ((fav ;; any results for the favorite bookmark item? (mu4e-last-query-results))))
(seq-find (lambda (bm) (plist-get bm :favorite)) (let* ((unread (plist-get fav :unread))
(mu4e-last-query-results)))) (count (plist-get fav :count))
(let* ((unread (plist-get fav :unread)) (baseline (plist-get fav :baseline))
(count (plist-get fav :count)) (baseline-unread
(baseline (plist-get fav :baseline)) (or (when baseline (plist-get baseline :unread)) unread))
(baseline-unread (delta (- unread baseline-unread)))
(or (when baseline (plist-get baseline :unread)) unread)) (propertize
(delta (- unread baseline-unread))) (format "%s%s%s/%s "
(propertize (funcall (if mu4e-use-fancy-chars 'cdr 'car)
(format "%s%s%s/%s " (cond
(funcall (if mu4e-use-fancy-chars 'cdr 'car) ((> delta 0) mu4e-modeline-new-items)
(cond ((> unread 0) mu4e-modeline-unread-items)
((> delta 0) mu4e-modeline-new-items) ((> count 0) mu4e-modeline-all-read)
((> unread 0) mu4e-modeline-unread-items) (t mu4e-modeline-all-clear)))
((> count 0) mu4e-modeline-all-read) (propertize (number-to-string unread) 'face 'mu4e-header-key-face)
(t mu4e-modeline-all-clear))) (if (<= delta 0) ""
(propertize (number-to-string unread) 'face 'mu4e-header-key-face) (propertize (format "(%+d)" delta)
(if (<= delta 0) "" 'face 'mu4e-unread-face))
(propertize (format "(%+d)" delta) (number-to-string count))
'face 'mu4e-unread-face)) 'help-echo (format "mu4e query: '%s'" (mu4e--bookmark-query fav))
(number-to-string count)) 'mouse-face 'mode-line-highlight
'help-echo (format "mu4e query: '%s'" (mu4e--bookmark-query fav)) 'keymap '(mode-line keymap
'mouse-face 'mode-line-highlight (mouse-1 . mu4e-jump-to-favorite)
'keymap '(mode-line keymap (mouse-2 . mu4e-jump-to-favorite)
(mouse-1 . mu4e-jump-to-favorite) (mouse-3 . mu4e-jump-to-favorite))))))
(mouse-2 . mu4e-jump-to-favorite)
(mouse-3 . mu4e-jump-to-favorite))))))))
(defun mu4e--modeline-update()
"Update the modeline and redisplay if mu4e-modeline-mode is
active."
(when mu4e-modeline-mode
(setq mu4e--bookmarks-modeline-cached nil) ;; force recalculation
(force-mode-line-update)))

View File

@ -146,6 +146,7 @@ non-nil."
(setq mu4e--context-current context) (setq mu4e--context-current context)
(run-hooks 'mu4e-context-changed-hook) (run-hooks 'mu4e-context-changed-hook)
(mu4e--modeline-update)
(mu4e-message "Switched context to %s" (mu4e-context-name context))) (mu4e-message "Switched context to %s" (mu4e-context-name context)))
context)) context))
@ -207,12 +208,13 @@ as it is."
(eval ,@body)))) (eval ,@body))))
(defun mu4e--context-modeline-item () (defun mu4e--context-modeline-item ()
"Propertized string with the current context name. "Propertized string with the current context or nil."
An empty string \"\" if there is none." (when (mu4e-context-current)
(if (mu4e-context-current) (concat
(concat "[" (propertize (mu4e-quote-for-modeline "["
(mu4e-context-name (mu4e-context-current))) (propertize (mu4e-quote-for-modeline
'face 'mu4e-context-face) "] " ) "")) (mu4e-context-name (mu4e-context-current)))
'face 'mu4e-context-face) "] " )))
(define-minor-mode mu4e-context-minor-mode (define-minor-mode mu4e-context-minor-mode
"Mode for switching the mu4e context." "Mode for switching the mu4e context."

View File

@ -130,6 +130,7 @@ This version handles updating the current screen as well."
(mu4e-context-minor-mode) (mu4e-context-minor-mode)
(mu4e-search-minor-mode) (mu4e-search-minor-mode)
(mu4e-update-minor-mode) (mu4e-update-minor-mode)
(mu4e-modeline-mode)
(setq-local revert-buffer-function (setq-local revert-buffer-function
(lambda (_ignore-auto _noconfirm) (lambda (_ignore-auto _noconfirm)
(mu4e--main-view 'refresh)))) (mu4e--main-view 'refresh))))
@ -352,13 +353,14 @@ character of the keyboard shortcut
(declare-function mu4e--start "mu4e") (declare-function mu4e--start "mu4e")
(defun mu4e--main-view (&optional refresh) (defun mu4e--main-view (&optional refresh no-reset)
"Create or refresh the mu4e main-view, and switch to it. "Create or refresh the mu4e main-view, and switch to it.
When REFRESH is non nil refresh infos from server. When REFRESH is non nil refresh infos from server.
If `mu4e-split-view' equals \='single-window, show a mu4e menu If `mu4e-split-view' equals \='single-window, show a mu4e menu
instead." instead."
(mu4e--reset-baseline) (unless no-reset
(mu4e--reset-baseline))
(if (eq mu4e-split-view 'single-window) (if (eq mu4e-split-view 'single-window)
(mu4e--main-menu) (mu4e--main-menu)
(let ((buf (get-buffer-create mu4e-main-buffer-name)) (let ((buf (get-buffer-create mu4e-main-buffer-name))

View File

@ -22,8 +22,8 @@
;;; Commentary: ;;; Commentary:
;; This file contains functionality for putting mu4e-related information ;; This file contains functionality for putting mu4e-related information in the
;; in the emacs modeline, both buffer-specific and globally. ;; Emacs modeline, both buffer-specific and globally.
;;; Code: ;;; Code:
@ -38,24 +38,35 @@ Each element is function that evaluates to a string.")
Each element is function that evaluates to a string.") Each element is function that evaluates to a string.")
(defun mu4e--modeline-register (func &optional global) (defun mu4e--modeline-register (func &optional global)
"Register a function for calculating some mu4e modeline part. "Register FUNC for calculating some mu4e modeline part.
If GLOBAL is non-nil, add to the global-modeline; otherwise use If GLOBAL is non-nil, add to the global-modeline; otherwise use
the buffer-local one." the buffer-local one."
(add-to-list (add-to-list
(if global 'mu4e--modeline-global-items 'mu4e--modeline-buffer-items) (if global
'mu4e--modeline-global-items
'mu4e--modeline-buffer-items)
func)) func))
(defvar mu4e--modeline-item nil (defvar mu4e--modeline-item nil
"Mu4e item for the global-mode-line.") "Mu4e item for the global-mode-line.")
(defvar mu4e--modeline-string-cached nil
"Cached version of the modeline string.")
(defun mu4e--modeline-string () (defun mu4e--modeline-string ()
"Calculate the current mu4e modeline string." "Get the current mu4e modeline string."
(mapconcat (or mu4e--modeline-string-cached
(lambda (item) (setq mu4e--modeline-string-cached
(if (functionp item) (mapconcat
(or (funcall item) "") (lambda (func) (or (funcall func) ""))
"")) (append mu4e--modeline-buffer-items
(append mu4e--modeline-buffer-items mu4e--modeline-global-items) " ")) mu4e--modeline-global-items)
" "))))
(defun mu4e--modeline-update ()
"Recalculate and force-update the modeline."
(setq mu4e--modeline-string-cached nil)
(force-mode-line-update))
(define-minor-mode mu4e-modeline-mode (define-minor-mode mu4e-modeline-mode
"Minor mode for showing mu4e information on the modeline." "Minor mode for showing mu4e information on the modeline."
@ -68,12 +79,14 @@ the buffer-local one."
(if mu4e-modeline-mode (if mu4e-modeline-mode
(progn (progn
(setq mu4e--modeline-item '(:eval (mu4e--modeline-string))) (setq mu4e--modeline-item '(:eval (mu4e--modeline-string)))
(add-to-list 'global-mode-string mu4e--modeline-item)) (add-to-list 'global-mode-string mu4e--modeline-item)
(mu4e--modeline-update))
(progn (progn
(setq global-mode-string (setq global-mode-string
(seq-remove (lambda (item) (equal item mu4e--modeline-item)) (seq-remove (lambda (item) (equal item mu4e--modeline-item))
global-mode-string)))) global-mode-string)))
(force-mode-line-update)) (force-mode-line-update)))
(provide 'mu4e-modeline) (provide 'mu4e-modeline)
;; mu4e-modeline.el ends here
;;; mu4e-modeline.el ends here

View File

@ -204,7 +204,8 @@ show the message with MSGID."
(mu4e-mark-handle-when-leaving) (mu4e-mark-handle-when-leaving)
(mu4e--search-execute expr ignore-history) (mu4e--search-execute expr ignore-history)
(setq mu4e--search-msgid-target msgid (setq mu4e--search-msgid-target msgid
mu4e--search-view-target show))) mu4e--search-view-target show)
(mu4e--modeline-update)))
(defun mu4e-search-edit () (defun mu4e-search-edit ()
"Edit the last search expression." "Edit the last search expression."
@ -459,6 +460,7 @@ If KEY is provided, use it instead of asking user."
(when choice (when choice
(set choice (not (symbol-value choice))) (set choice (not (symbol-value choice)))
(mu4e-message "Set `%s' to %s" (symbol-name choice) (symbol-value choice)) (mu4e-message "Set `%s' to %s" (symbol-name choice) (symbol-value choice))
(mu4e--modeline-update)
(unless dont-refresh (unless dont-refresh
(mu4e-search-rerun))))) (mu4e-search-rerun)))))

View File

@ -156,10 +156,11 @@ Invoke FUNC if non-nil."
;; (if any) and the modeline. ;; (if any) and the modeline.
;; 1. update the query results (i.e. process the new server queries) ;; 1. update the query results (i.e. process the new server queries)
(mu4e-last-query-results 'force-update) (mu4e-last-query-results 'refresh)
(unless mu4e--baseline (if mu4e--baseline
(mu4e--reset-baseline)) (mu4e-last-query-results 'refresh)
(mu4e--modeline-update) (mu4e--reset-baseline))
;; note: mu4e-reset-baseline implies mu4e--last-query-results
;; 2. update the main view, if any ;; 2. update the main view, if any
(when (buffer-live-p mu4e-main-buffer-name) (when (buffer-live-p mu4e-main-buffer-name)
@ -261,7 +262,7 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(when (and (buffer-live-p mainbuf) (get-buffer-window mainbuf)) (when (and (buffer-live-p mainbuf) (get-buffer-window mainbuf))
(save-window-excursion (save-window-excursion
(select-window (get-buffer-window mainbuf)) (select-window (get-buffer-window mainbuf))
(mu4e--main-view 'refresh)))))) (mu4e--main-view 'refresh 'no-reset))))))
((plist-get info :message) ((plist-get info :message)
(mu4e-index-message "%s" (plist-get info :message)))))) (mu4e-index-message "%s" (plist-get info :message))))))