mu4e: implement mu4e-query-items

Implement function / datas structure to conveniently aggregate all
query-related data, for reuse in various places in mu4e...

... to start with the main menu, which gets much simpler. And the
modeline.
This commit is contained in:
Dirk-Jan C. Binnema
2023-01-09 23:58:10 +02:00
parent fc867c9065
commit 10041eb2e1
10 changed files with 345 additions and 281 deletions

View File

@ -24,8 +24,9 @@
;;; Code:
(require 'mu4e-helpers)
(require 'mu4e-server)
(require 'mu4e-modeline)
(require 'mu4e-folders)
(require 'mu4e-query-items)
;;; Configuration
@ -60,9 +61,11 @@ Note that the :query parameter can be a function/lambda.
Optionally, you can add the following:
- `:favorite' - if t, monitor the results of this query, and make
it eligible for showing its status in the emacs modeline. At mose
it eligible for showing its status in the modeline. At mose
one bookmark should have this set to t (otherwise the _first_
bookmark is the implicit favorite)
bookmark is the implicit favorite). The query for the `:favorite'
item must be unique among `mu4e-bookmarks' and
`mu4e-maildir-shortcuts'.
- `:hide' - if t, the bookmark is hidden from the main-view and
speedbar.
- `:hide-unread' - do not show the counts of
@ -80,37 +83,23 @@ query."
(defun mu4e-ask-bookmark (prompt)
"Ask the user for a bookmark (using PROMPT) as defined in
`mu4e-bookmarks', then return the corresponding query."
"Ask user for bookmark using PROMPT.
Return the corresponding query. The bookmark are as defined in
`mu4e-bookmarks'."
(unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined"))
(let* ((prompt (mu4e-format "%s" prompt))
(bmarks
(mapconcat
(lambda (bm)
(concat
"[" (propertize (make-string 1 (plist-get bm :key))
'face 'mu4e-highlight-face)
"["
(propertize (make-string 1 (plist-get bm :key))
'face 'mu4e-highlight-face)
"]"
(plist-get bm :name))) (mu4e-bookmarks) ", "))
(kar (read-char (concat prompt bmarks))))
(mu4e-get-bookmark-query kar)))
(defun mu4e--bookmark-query (bm)
"Get query string for some bookmark."
(when bm
(let* ((query (or (plist-get bm :query)
(mu4e-warn "No query in %S" bm)))
;; queries being functions is deprecated.
(query (if (functionp query) (funcall query) query)))
;; earlier, we allowed for the queries being fucntions
(unless (stringp query)
(mu4e-warn "Could not get query string from %s" bm))
;; apparently, non-UTF8 queries exist, i.e.,
;; with maild dir names.
(decode-coding-string query 'utf-8 t))))
(defun mu4e-get-bookmark-query (kar)
"Get the corresponding bookmarked query for shortcut KAR.
Raise an error if none is found."
@ -146,88 +135,14 @@ Convert from the old format if needed."
item))
mu4e-bookmarks))
(defun mu4e-favorite-bookmark ()
"Find the favorite bookmark.
The favorit bookmark is the first one that has a non-nil
':favorite' property, or the first if there is none."
(let ((bookmarks (mu4e-bookmarks)))
(or (seq-find (lambda (bm) (plist-get bm :favorite))
(mu4e-bookmarks))
(car-safe bookmarks))))
;;; Last & baseline query results for bookmarks.
(defvar mu4e--baseline nil
"Some previous version of the query-results.
This is used as the baseline to track updates by comparing it to
the latest query-results.")
(defvar mu4e--baseline-tstamp nil
"Timestamp for when the query-results baseline was updated.")
(defun mu4e--reset-baseline ()
(setq mu4e--baseline (mu4e-server-query-results)
mu4e--baseline-tstamp (current-time))
(mu4e-last-query-results 'refresh)) ; for side-effects
(defvar mu4e--last-query-results-cached nil)
(defun mu4e-last-query-results(&optional refresh)
"Get the results (counts) of the latest queries.
Either read form the cache or update them when oudated or FORCE
is non-nil.
The queries are the bookmark / maildir queries that are used to
populate the read/unread counts in the main view and modeline.
They are refreshed when calling `(mu4e)', i.e., when going to the
main view.
When available, the baseline results are added as well.
The results are a list of elements of the form
(:query \"query string\"
:count <total number matching count>
:unread <number of unread messages in count>
[:favorite t]
:baseline ( ;; baseline results
:count <total number matching count>
:unread <number of unread messages in count>)) The
baseline part is optional (see `mu4e-reset-query-results') for
more details).
Uses a cached string unless it is nil or REFRESH is non-nil."
(or (and (not refresh) mu4e--last-query-results-cached)
(setq mu4e--last-query-results-cached
(let* ((favorite (mu4e-favorite-bookmark))
(favorite-query
(and favorite (mu4e--bookmark-query favorite))))
;; walk over the remembered queries
;; and augment them with the baseline data and ':favorite' flag, if
;; any.
(seq-map
(lambda (qres)
;; note: queries can be _functions_ too; use their
;; string value.
(let* ((query (mu4e--bookmark-query qres))
(bres (seq-find ;; find the corresponding baseline entry
(lambda (bq)
(string= query (mu4e--bookmark-query bq)))
mu4e--baseline)))
(when (string= query (or favorite-query ""))
(plist-put qres :favorite t))
(when bres
(plist-put qres :baseline
`(:count ,(plist-get bres :count)
:unread ,(plist-get bres :unread))))
qres))
(mu4e-server-query-results))))))
(defun mu4e-last-query-result (query)
"Get the last result for some QUERY or nil if not found.
See `mu4e-last-query-results' for the format."
(defun mu4e-bookmark-favorite ()
"Find the favorite bookmark."
;; note, use query-items, which will have picked a favorite
;; even if user did not provide one explictly
(seq-find
(lambda (elm) (string= query (mu4e--bookmark-query elm)))
(mu4e-last-query-results)))
(lambda (item)
(plist-get item :favorite))
(mu4e-query-items 'bookmarks)))
;; for Zero-Inbox afficionados
(defvar mu4e-modeline-all-clear '("C:" . "🌀")
@ -244,7 +159,7 @@ I.e., very new messages.")
(defun mu4e-jump-to-favorite ()
"Jump to to the favorite bookmark, if any."
(interactive)
(when-let ((fav (mu4e--bookmark-query (mu4e-favorite-bookmark))))
(when-let ((fav (mu4e--bookmark-query (mu4e-bookmark-favorite))))
(mu4e-search-bookmark fav)))
(defun mu4e--bookmarks-modeline-item ()
@ -254,34 +169,24 @@ This uses the one special ':favorite' bookmark, and if there is
one, creates a propertized string for display in the modeline."
(when-let ((fav ;; any results for the favorite bookmark item?
(seq-find (lambda (bm) (plist-get bm :favorite))
(mu4e-last-query-results))))
(let* ((unread (plist-get fav :unread))
(count (plist-get fav :count))
(baseline (plist-get fav :baseline))
(baseline-unread
(or (when baseline (plist-get baseline :unread)) unread))
(delta (- unread baseline-unread)))
(mu4e-query-items 'bookmarks))))
(cl-destructuring-bind (&key unread count delta-unread
&allow-other-keys) fav
(propertize
(format "%s%s%s/%s "
(format "%s%s "
(funcall (if mu4e-use-fancy-chars 'cdr 'car)
(cond
((> delta 0) mu4e-modeline-new-items)
((> delta-unread 0) mu4e-modeline-new-items)
((> unread 0) mu4e-modeline-unread-items)
((> count 0) mu4e-modeline-all-read)
(t mu4e-modeline-all-clear)))
(propertize (number-to-string unread) 'face 'mu4e-header-key-face)
(if (<= delta 0) ""
(propertize (format "(%+d)" delta)
'face 'mu4e-unread-face))
(number-to-string count))
(mu4e--query-item-display-counts fav))
'help-echo (format "mu4e query: '%s'" (mu4e--bookmark-query fav))
'mouse-face 'mode-line-highlight
'keymap '(mode-line keymap
(mouse-1 . mu4e-jump-to-favorite)
(mouse-2 . mu4e-jump-to-favorite)
(mouse-3 . mu4e-jump-to-favorite))))))
(provide 'mu4e-bookmarks)
;;; mu4e-bookmarks.el ends here