mu4e: support showing 'baseline' query-results

Add the concept of a query results baseline, i.e., the result for
bookmark-queries at some particular point in time. Later, we can compare
the results with the then-current query-results.

Show the delta in the main view. Add mu4e-reset-baseline-query-results
to reset the baseline to 'now'. By default, we automatically reset when
explicitly (interactively) going to the main-view, i.e., M-x mu4e.
This commit is contained in:
Dirk-Jan C. Binnema 2022-12-31 17:47:33 +02:00
parent b7b97212ce
commit ba5026e7dc
5 changed files with 118 additions and 39 deletions

View File

@ -86,8 +86,6 @@ marked as read-only, or non-nil otherwise."
function)
:group 'mu4e-view)
(defun mu4e-select-other-view ()
@ -240,7 +238,32 @@ Function returns the cdr of the list element."
;;; Server properties
(defvar mu4e--server-props nil
"Metadata we receive from the mu4e server.")
"Metadata we receive from the mu4e server.
Use `mu4e--update-server-props' to update.")
;; XXX: we could make these session-persistent
(defvar mu4e--baseline-query-results 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-query-results-tstamp nil
"Timestamp for when the query-results baseline was updated.")
(defun mu4e-reset-baseline-query-results ()
"Reset the baseline query-results."
(interactive)
(setq mu4e--baseline-query-results nil
mu4e--baseline-query-results-tstamp nil))
(defun mu4e--update-server-props (props)
"Update server props and possibly the baseline query results."
(setq mu4e--server-props props)
(when-let ((queries (plist-get mu4e--server-props :queries)))
(unless mu4e--baseline-query-results
(setq mu4e--baseline-query-results queries
mu4e--baseline-query-results-tstamp (current-time)))))
(defun mu4e-server-properties ()
"Get the server metadata plist."
@ -272,16 +295,38 @@ used to populated the read/unread counts in the main view. They
are refreshed when calling `(mu4e)', i.e., when going to the main
view.
When available, the based-line 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>)"
(plist-get mu4e--server-props :queries))
:count <total number matching count>
:unread <number of unread messages in count>
:baseline ( ;; baseline results
:count <total number matching count>
:unread <number of unread messages in count>)) The
baseline part is optional (see
`mu4e-reset-baseline-query-results') for more details)."
(unless mu4e--baseline-query-results
(mu4e-reset-baseline-query-results))
(seq-map (lambda (qres)
(let* ((query (plist-get qres :query))
(bres (seq-find ;; find the corresponding baseline entry
(lambda (bq) (string= query (plist-get bq :query)))
mu4e--baseline-query-results)))
(when bres
(plist-put qres :baseline
`(:count ,(plist-get bres :count)
:unread ,(plist-get bres :unread))))
qres))
(plist-get mu4e--server-props :queries)))
(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.
See `mu4e-last-query-results' for the format."
(seq-find
(lambda (elm) (string= (plist-get elm :query) query))
(lambda (elm) ;;; XXX do we need the decoding?
(let ((qstring (decode-coding-string (plist-get elm :query) 'utf-8 t)))
(string= query qstring)))
(mu4e-last-query-results)))

View File

@ -44,15 +44,31 @@
;; Configuration
(defvar mu4e-main-hide-personal-addresses nil
(defcustom mu4e-main-hide-personal-addresses nil
"Whether to hide the personal address in the main view.
This can be useful to avoid the noise when there are many, and
This can be useful to avoid the noise when there are many, and
also hides the warning if your `user-mail-address' is not part of
the personal addresses.")
the personal addresses."
:type 'boolean
:group 'mu4e-main)
(defvar mu4e-main-hide-fully-read nil
"Whether to hide bookmarks or maildirs without unread messages.")
(defcustom mu4e-main-hide-fully-read nil
"Whether to hide bookmarks or maildirs without unread messages."
:type 'boolean
:group 'mu4e-main)
(defcustom mu4e-main-hide-baseline-delta nil
"Whether to hide the baseline-from-delta from the message counts
for bookmarks and maildirs."
:type 'boolean
:group 'mu4e-main)
(defcustom mu4e-main-auto-reset-baseline t
"Automatically reset the baseline when explicitly (interactively)
swiching to the main-view (using the `mu4e' command."
:type 'boolean
:group 'mu4e-main)
;;; Mode
@ -83,6 +99,12 @@ the personal addresses.")
(interactive)
(mu4e-info (concat mu4e-doc-dir "/NEWS.org")))
(defun mu4e--main-reset-baseline-query-results ()
"Main-view version of `mu4e-reset-baseline-query-results'.
This version handles updating the current screen as well."
(interactive)
(mu4e-reset-baseline-query-results)
(revert-buffer))
(defvar mu4e-main-mode-map
(let ((map (make-sparse-keymap)))
@ -95,6 +117,7 @@ the personal addresses.")
(define-key map "f" #'smtpmail-send-queued-mail)
;;
(define-key map "U" #'mu4e-update-mail-and-index)
(define-key map "R" #'mu4e--main-reset-baseline-query-results)
(define-key map (kbd "C-S-u") #'mu4e-update-mail-and-index)
;; for terminal users
(define-key map (kbd "C-c C-u") #'mu4e-update-mail-and-index)
@ -102,7 +125,7 @@ the personal addresses.")
(define-key map "S" #'mu4e-kill-update-mail)
(define-key map (kbd "C-S-u") #'mu4e-update-mail-and-index)
(define-key map ";"
(lambda()(interactive)(mu4e-context-switch)(revert-buffer)))
(lambda()(interactive)(mu4e-context-switch)(revert-buffer)))
(define-key map "$" #'mu4e-show-log)
(define-key map "A" #'mu4e-about)
@ -122,7 +145,6 @@ the personal addresses.")
(set (make-local-variable 'revert-buffer-function) #'mu4e--main-view-real)
(add-hook 'mu4e-index-updated-hook #'mu4e--main-update-after-index))
(defun mu4e--main-action-str (str &optional func-or-shortcut)
"Highlight the first occurrence of [.] in STR.
If FUNC-OR-SHORTCUT is non-nil and if it is a function, call it
@ -163,7 +185,7 @@ clicked."
maximize (string-width (plist-get b :name))))
(defun mu4e--main-item (fullkey name qcounts max-length)
"Display one main bookmarks/maildir item.
"Display a main-view bookmarks/maildir item.
- FULLKEY is a 2-character string describing the item's shortcut
- NAME is the name of the of the item
- QCOUNTS is a structure with unread information
@ -175,22 +197,31 @@ clicked."
(concat "\t* [" fullkey "] " name) fullkey)
;; append all/unread numbers, if available.
(if qcounts
(let ((unread (plist-get (car qcounts) :unread))
(count (plist-get (car qcounts) :count)))
(format
"%s (%s/%s)"
(make-string (- max-length (string-width name)) ? )
(propertize (number-to-string unread)
'face 'mu4e-header-key-face)
count))
(let* ((unread (plist-get qcounts :unread))
(count (plist-get qcounts :count))
(baseline (plist-get qcounts :baseline))
(baseline-unread
(or (when baseline (plist-get baseline :unread)) unread))
(delta (- unread baseline-unread)))
(format"%s (%s%s/%s)"
(make-string (- max-length (string-width name)) ? )
(propertize
(number-to-string unread) 'face 'mu4e-header-key-face
'help-echo "Number of unread messages")
(if (not mu4e-main-hide-baseline-delta)
(propertize (format "/%+d" delta) 'face
(if (> delta 0) 'mu4e-unread-face 'default)
'help-echo "Unread messsages baseline-delta")
"")
(propertize (number-to-string count)
'help-echo "Total number of messages")))
"") "\n"))
(defun mu4e--main-items (shortcut items queries max-length)
(defun mu4e--main-items (shortcut items max-length)
"Display the entries for the bookmark/maildir menu.
- SHORTCUT is a single character which is the first
character of the keyboard shortcut
- ITEMS is a list of items, for format see `(mu4e-bookmarks)'.
- QUERIES is the list of last query-results (or nil)
- MAX-LENGTH is the maximum length for an item name
(used for alignment)."
(cl-loop for item in items
@ -198,13 +229,7 @@ character of the keyboard shortcut
for name = (plist-get item :name)
for query = (funcall (or mu4e-query-rewrite-function #'identity)
(plist-get item :query))
for qcounts = (and (stringp query)
(cl-loop for q in queries
when (string=
(decode-coding-string
(plist-get q :query) 'utf-8 t)
query)
collect q))
for qcounts = (mu4e-last-query-result query)
for unread = (and qcounts (plist-get (car qcounts) :unread))
when (not (plist-get item :hide))
when (not (and mu4e-main-hide-fully-read (eq unread 0)))
@ -261,11 +286,10 @@ When REFRESH is non nil refresh infos from server."
"\t* [C]ompose a new message\n" #'mu4e-compose-new)
"\n"
(propertize " Bookmarks\n\n" 'face 'mu4e-title-face)
(mu4e--main-items ?b (mu4e-bookmarks) (mu4e-last-query-results) max-length)
(mu4e--main-items ?b (mu4e-bookmarks) max-length)
"\n"
(propertize " Maildirs\n\n" 'face 'mu4e-title-face)
(mu4e--main-items ?j (mu4e--maildirs-with-query)
(plist-get mu4e--server-props :queries) max-length)
(mu4e--main-items ?j (mu4e--maildirs-with-query) max-length)
"\n"
(propertize " Misc\n\n" 'face 'mu4e-title-face)
@ -274,7 +298,9 @@ When REFRESH is non nil refresh infos from server."
(mu4e-context-switch)(revert-buffer)))
(mu4e--main-action-str "\t* [U]pdate email & database\n"
'mu4e-update-mail-and-index)
#'mu4e-update-mail-and-index)
(mu4e--main-action-str "\t* [R]eset query-results baseline\n"
#'mu4e--main-reset-baseline-query-results)
;; show the queue functions if `smtpmail-queue-dir' is defined
(if (file-directory-p smtpmail-queue-dir)
@ -291,6 +317,11 @@ When REFRESH is non nil refresh infos from server."
(mu4e--key-val "last updated"
(current-time-string
(plist-get mu4e-index-update-status :tstamp)))
(if (and (not mu4e-main-hide-baseline-delta)
mu4e--baseline-query-results-tstamp)
(mu4e--key-val "baseline"
(current-time-string mu4e--baseline-query-results-tstamp))
"")
(mu4e--key-val "database-path" (mu4e-database-path))
(mu4e--key-val "maildir" (mu4e-root-maildir))
(mu4e--key-val "in store"

View File

@ -286,7 +286,7 @@ The server output is as follows:
;; received a pong message
((plist-get sexp :pong)
(setq mu4e--server-props (plist-get sexp :props))
(mu4e--update-server-props (plist-get sexp :props))
(funcall mu4e-pong-func sexp))
;; received a contacts message

View File

@ -1,6 +1,6 @@
;;; mu4e-update.el -- part of mu4e, -*- lexical-binding: t -*-
;; Copyright (C) 2011-2021 Dirk-Jan C. Binnema
;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -311,6 +311,7 @@ run in the background; otherwise, pop up a window."
:lighter ""
:keymap
(let ((map (make-sparse-keymap)))
(define-key map (kbd "C-c C-r") #'mu4e-reset-baseline-query-results)
(define-key map (kbd "C-S-u") #'mu4e-update-mail-and-index)
;; for terminal users
(define-key map (kbd "C-c C-u") #'mu4e-update-mail-and-index)

View File

@ -139,6 +139,8 @@ invoke
(unless (mu4e-context-current)
(mu4e--context-autoswitch nil mu4e-context-policy))
(setq mu4e-pong-func (lambda (info) (mu4e--pong-handler info func)))
(when mu4e-main-auto-reset-baseline
(mu4e-reset-baseline-query-results))
(mu4e--server-ping
(mapcar ;; send it a list of queries we'd like to see read/unread info for
(lambda (bm)