mu4e: show short counts next to maildir/bookmark entry

When you ask for bookmarks or maildirs through mu4e-ask-bookmark,
mu4e-ask-maildir, the counts are displayed in the (default) completions
next to the maildirs. This is a shorter version of the full display,
just showing either the delta or the number of unread messages, if any.
If you don't want to see these counts, there is mu4e-hide-short-counts.
This commit is contained in:
Dirk-Jan C. Binnema
2025-02-04 22:12:00 +02:00
parent 4b691f97cc
commit 055cb774e0
7 changed files with 102 additions and 27 deletions

View File

@ -1,6 +1,6 @@
;;; mu4e-bookmarks.el --- Bookmarks handling -*- lexical-binding: t -*-
;; Copyright (C) 2011-2023 Dirk-Jan C. Binnema
;; Copyright (C) 2011-2025 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -28,7 +28,6 @@
(require 'mu4e-folders)
(require 'mu4e-query-items)
;;; Configuration
(defgroup mu4e-bookmarks nil
@ -79,19 +78,33 @@ query."
:type '(repeat (plist))
:group 'mu4e-bookmarks)
(declare-function mu4e-query-items "mu4e-query-items")
(declare-function mu4e--query-item-display-short-counts "mu4e-query-items")
(defun mu4e-ask-bookmark (prompt)
"Ask user for bookmark using PROMPT.
Return the corresponding query. The bookmark are as defined in
`mu4e-bookmarks'."
`mu4e-bookmarks'.
The names of the bookmarks are displayed in the minibuffer,
suffixed with the short version of the unread counts, as per
`mu4e--query-item-display-short-counts'."
(unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined"))
(let* ((bmarks (seq-map (lambda (bm)
(cons (format "%c%s"
(plist-get bm :key)
(plist-get bm :name))
(plist-get bm :query)))
(mu4e-filter-single-key (mu4e-bookmarks)))))
(mu4e-read-option prompt bmarks)))
(let* ((bmarks
(seq-map
(lambda (bm) ;; find query-item for bookmark
(let* ((qitem (seq-find
(lambda (qitem)
(equal (plist-get bm :query) (plist-get qitem :query)))
(mu4e-query-items 'bookmarks)))
(unreads (mu4e--query-item-display-short-counts qitem)))
(cons (format "%c%s%s"
(plist-get bm :key)
(plist-get bm :name)
unreads)
(plist-get bm :query))))
(mu4e-filter-single-key (mu4e-bookmarks)))))
(mu4e-read-option prompt bmarks)))
(defun mu4e-get-bookmark-query (kar)
"Get the corresponding bookmarked query for shortcut KAR.
@ -190,6 +203,5 @@ one, creates a propertized string for display in the modeline."
(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

View File

@ -1,6 +1,6 @@
;;; mu4e-context.el --- Switching between settings -*- lexical-binding: t -*-
;; Copyright (C) 2015-2023 Dirk-Jan C. Binnema
;; Copyright (C) 2015-2025 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -87,6 +87,9 @@ none."
(cl-defstruct mu4e-context
"A mu4e context object with the following members:
- `name': the name of the context, eg. \"Work\" or \"Private\".
When using mu4e's default completion, it uses the first letter of
the name for this, so you should ensure those are different for
all contexts.
- `enter-func': a parameterless function invoked when entering
this context, or nil
- `leave-func':a parameterless function invoked when leaving this
@ -104,7 +107,6 @@ none."
;; if it matches, nil otherwise
vars) ;; alist of variables.
(defun mu4e--context-ask-user (prompt)
"Let user choose some context based on its name with PROMPT."
(when mu4e-contexts
@ -168,7 +170,7 @@ match, return the first. For MSG and POLICY, see
nil (mu4e-context-name context))))))
(defun mu4e-context-determine (msg &optional policy)
"Return the first context where match-func evaluate to non-nil.
"Return first context for which match-func returns non-nil.
MSG points to the plist for the message replied to or forwarded,
or nil if there is no such MSG; similar to what

View File

@ -177,6 +177,16 @@ Converts from the old format if needed."
item))
mu4e-maildir-shortcuts))
(declare-function mu4e-query-items "mu4e-query-items")
(declare-function mu4e--query-item-display-short-counts "mu4e-query-items")
(defun mu4e--query-item-for-maildir-shortcut (mds)
"Find the corresponding query-item for some maildir shortcut MDS.
This is based on their query. Return nil if not found."
(seq-find (lambda (qitem)
(equal (plist-get qitem :maildir) (plist-get mds :maildir)))
(mu4e-query-items 'maildirs)))
;; the standard folders can be functions too
(defun mu4e--get-folder (foldervar msg)
"Within the mu-context of MSG, get message folder FOLDERVAR.
@ -231,7 +241,7 @@ to create it; otherwise return nil."
(let ((seems-to-exist (file-directory-p dir)))
(when (or seems-to-exist
(yes-or-no-p (mu4e-format "%s does not exist yet. Create now?" dir)))
;; even when the maildir already seems to exist, call mkdir for a deeper
;; even when the maildir already seems to exist, call mkdir for a deepe
;; check. However only get an update when the maildir is totally new.
(mu4e--server-mkdir dir (not seems-to-exist))
t)))
@ -245,14 +255,23 @@ to create it; otherwise return nil."
If the special shortcut \"o\" (for _o_ther) is used, or
if (mu4e-maildir-shortcuts) evaluates to nil, let user choose
from all maildirs under `mu4e-maildir'."
from all maildirs under `mu4e-maildir'.
The names of the maildirs are displayed in the minibuffer,
suffixed with the short version of the unread counts, as per
`mu4e--query-item-display-short-counts'."
(let* ((options
(seq-map (lambda (md)
(cons
(format "%c%s" (plist-get md :key)
(or (plist-get md :name)
(plist-get md :maildir)))
(plist-get md :maildir)))
(seq-map
(lambda (md)
(let* ((qitem (mu4e--query-item-for-maildir-shortcut md))
(unreads (mu4e--query-item-display-short-counts qitem)))
(cons
(format "%c%s%s"
(plist-get md :key)
(or (plist-get md :name)
(plist-get md :maildir))
unreads)
(plist-get md :maildir))))
(mu4e-filter-single-key (mu4e-maildir-shortcuts))))
(response
(if (not options)

View File

@ -1,6 +1,6 @@
;;; mu4e-query-items.el --- Manage query results -*- lexical-binding: t -*-
;; Copyright (C) 2023-2024 Dirk-Jan C. Binnema
;; Copyright (C) 2023-2025 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -61,6 +61,12 @@ incorrect results for the various unread counts."
:type 'function
:group 'mu4e-search)
(defcustom mu4e-hide-short-counts nil
"Hide the short count of unread messages.
As used in `mu4e-ask-bookmark' and `mu4e-ask-folder'."
:type 'boolean
:group 'mu4e)
(defvar mu4e--query-items-baseline nil
"Some previous version of the query-items.
This is used as the baseline to track updates by comparing it to
@ -98,7 +104,9 @@ If ITEMS does not yet have a favorite item, pick the first."
(declare-function mu4e-maildir-shortcuts "mu4e-folders")
(defun mu4e--query-item-display-counts (item)
"Get the count display string for some query-data ITEM."
"Get the count display string for some query-data ITEM.
If the items has its `:hide-unread' at a non-nil value, return
an empty string."
;; purely for display, but we need it in the main menu, modeline
;; so let's keep it consistent.
(cl-destructuring-bind (&key unread hide-unread delta-unread count
@ -116,6 +124,25 @@ If ITEMS does not yet have a favorite item, pick the first."
(propertize (number-to-string count)
'help-echo "Total number")))))
(defun mu4e--query-item-display-short-counts (item)
"Get the short count display string for some query-data ITEM.
This gets the delta if it is greater than zero. Otherwise, the
total unread count if is greater than zero. Otherwise, an empty
string.
If the items has its `:hide-unread' at a non-nil value, or if
`mu4e-hide-short-counts' is non-nil, returns an empty string."
(cl-destructuring-bind (&key unread hide-unread delta-unread
&allow-other-keys) item
(if (or hide-unread mu4e-hide-short-counts)
""
(concat
(if (> (or delta-unread 0) 0)
(concat "(" (propertize (format "+%d" delta-unread) 'face 'mu4e-unread-face) ")")
(if (> (or unread 0) 0)
(concat "(" (propertize (format "%d" unread) 'face 'mu4e-header-key-face) ")")
""))))))
(defun mu4e--query-items-refresh (&optional reset-baseline)
"Get the latest query data from the mu4e server.
With RESET-BASELINE, reset the baseline first."
@ -210,7 +237,8 @@ bookmark or maildir."
;; useful for debugging.
(unless (string= query effective-query)
(plist-put value :effective-query effective-query))
;;for matching maildir shortcuts
(when maildir (plist-put value :maildir maildir))
;; nil props bring me discomfort
(when (plist-get item :favorite)
(plist-put value :favorite t))

View File

@ -1,6 +1,6 @@
;;; mu4e-search.el --- Search-related functions -*- lexical-binding: t -*-
;; Copyright (C) 2021,2024 Dirk-Jan C. Binnema
;; Copyright (C) 2021-2025 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>

View File

@ -847,7 +847,7 @@ attention:
;; bookmark for message that require quick attention
'( :name "Urgent"
:key ?u
:query "maildir:/inbox AND from:boss@@exmaple.com"))
:query "maildir:/inbox AND from:boss@@example.com"))
@end lisp
Note that @t{mu4e} resets the baseline when you are interacting with it (for
@ -859,6 +859,13 @@ as being a special kind of bookmark query that matches a Maildir. You can
configure this using the variable @code{mu4e-maildir-shortcuts}; see its
docstring and @ref{Maildir searches} for more details.
When you ask for bookmarks or maildirs through @code{mu4e-ask-bookmark},
@code{mu4e-ask-maildir}, the counts are displayed in the (default) completions
next to the maildir or bookmark entry. This is a shorter version of the full
display, just showing either the delta or the number of unread messages, if any.
If you do not want to see these counts, set @t{mu4e-hide-short-counts} to
non-@t{nil}.
@node Miscellaneous
@section Miscellaneous
@ -2610,6 +2617,8 @@ Let's see what's contained in a context. Most of it is optional.
A @code{mu4e-context} is Lisp object with the following members:
@itemize
@item @t{name}: the name of the context, e.g. @t{work} or @t{private}
in the default completion UI, @t{mu4e} uses the first letter of the context to
select them, so you should ensure all start with a different letter
@item @t{vars}:
an association-list (alist) of variable settings for this account.
@item @t{enter-func}: