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

@ -48,6 +48,7 @@ mu4e_srcs=[
'mu4e-modeline.el',
'mu4e-obsolete.el',
'mu4e-org.el',
'mu4e-query-items.el',
'mu4e-search.el',
'mu4e-server.el',
'mu4e-speedbar.el',

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

View File

@ -159,18 +159,6 @@ Converts from the old format if needed."
item))
mu4e-maildir-shortcuts))
(defun mu4e--maildirs-with-query ()
"Like `mu4e-maildir-shortcuts', but with :query populated.
This is compatibile with `mu4e-bookmarks'."
(seq-map
(lambda (item)
(let* ((maildir (plist-get item :maildir))
(name (or (plist-get item :name) maildir))
(item (plist-put item :name name))
(item (plist-put item :query (format "maildir:\"%s\"" maildir))))
item)) ;; we don't need ":maildir", but it's harmless.
(mu4e-maildir-shortcuts)))
;; the standard folders can be functions too
(defun mu4e--get-folder (foldervar msg)
"Within the mu-context of MSG, get message folder FOLDERVAR.

View File

@ -1526,13 +1526,12 @@ region if there is a region, then move to the next message."
(when mu4e-headers-advance-after-mark (mu4e-headers-next)))
(defun mu4e~headers-quit-buffer ()
"Quit the mu4e-headers buffer.
This is a rather complex function, to ensure we don't disturb
other windows."
"Quit the mu4e-headers buffer."
(interactive)
(mu4e-mark-handle-when-leaving)
(quit-window t)
(mu4e--main-view 'refresh))
(mu4e--query-items-reset-baseline)
(mu4e--main-view))
;;; Loading messages

View File

@ -27,6 +27,7 @@
(require 'smtpmail)
(require 'mu4e-helpers)
(require 'mu4e-context)
(require 'mu4e-compose)
(require 'mu4e-bookmarks)
(require 'mu4e-folders)
(require 'mu4e-update)
@ -34,6 +35,7 @@
(require 'mu4e-search)
(require 'mu4e-vars) ;; mu-wide variables
(require 'mu4e-window)
(require 'mu4e-query-items)
(declare-function mu4e-compose-new "mu4e-compose")
(declare-function mu4e-quit "mu4e")
@ -87,10 +89,9 @@ the personal addresses."
(mu4e-info (concat mu4e-doc-dir "/NEWS.org")))
(defun mu4e--main-reset-baseline()
"Main-view version of `mu4e-reset-query-results'.
This version handles updating the current screen as well."
"Reset the query baseline."
(interactive)
(mu4e--reset-baseline)
(mu4e--query-items-reset-baseline)
(revert-buffer))
(defvar mu4e-main-mode-map
@ -111,7 +112,8 @@ This version handles updating the current screen as well."
(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)
@ -137,6 +139,7 @@ This version handles updating the current screen as well."
["Quit" mu4e-quit :help "Quit mu4e"]
)))
(declare-function mu4e--server-bookmarks-queries "mu4e")
(define-derived-mode mu4e-main-mode special-mode "mu4e:main"
"Major mode for the mu4e main screen.
@ -146,10 +149,14 @@ This version handles updating the current screen as well."
(mu4e-context-minor-mode)
(mu4e-search-minor-mode)
(mu4e-update-minor-mode)
(mu4e-modeline-mode)
(setq-local revert-buffer-function
(lambda (_ignore-auto _noconfirm)
(mu4e--main-view 'refresh))))
;; the query results will trigger a redraw
(mu4e--query-items-refresh)))
(add-hook 'mu4e-query-items-updated-hook
(lambda ()
(when (get-buffer mu4e-main-buffer-name)
(mu4e--main-redraw-buffer))) nil t))
(defun mu4e--main-action-str (str &optional func-or-shortcut)
"Highlight the first occurrence of [.] in STR.
@ -183,64 +190,28 @@ clicked. If HIGHLIGHT is non-nil, hightlight the name."
(- (length newstr) 1))
'mouse-face 'highlight newstr) newstr))
(defun mu4e--longest-of-maildirs-and-bookmarks ()
"Return the length of longest name of bookmarks and maildirs."
(cl-loop for b in (append (mu4e-bookmarks)
(mu4e--maildirs-with-query))
maximize (string-width (plist-get b :name))))
(defun mu4e--main-item (fullkey name qcounts max-length)
"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
for this item (or nil)
- MAX-LENGTH is the maximum length for an item name
(used for alignment)."
(concat
(mu4e--main-action-str
(format "\t* [%s] %s" fullkey
(propertize name 'face
(if (and qcounts (plist-get qcounts :favorite))
'mu4e-header-key-face nil)))
fullkey)
;; append all/unread numbers, if available.
(if qcounts
(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 (> delta 0)
(propertize (format "(%+d)" delta) 'face
'mu4e-unread-face) "")
(propertize (number-to-string count)
'help-echo "Total number of messages")))
"") "\n"))
(defun mu4e--main-items (shortcut items max-length)
(defun mu4e--main-items (data shortcut 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)'
- MAX-LENGTH is the maximum length for an item name
(used for alignment)."
(cl-loop for item in items
for fullkey = (format "%c%c" shortcut (plist-get item :key))
for name = (plist-get item :name)
for query = (funcall (or mu4e-query-rewrite-function #'identity)
(mu4e--bookmark-query item))
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)))
concat (mu4e--main-item fullkey name qcounts max-length)))
- ITEMS is a list of items, for format see `(mu4e-query-data)'
- MAX-LENGTH is the maximum length for an item name"
(mapconcat
(lambda (item)
(cl-destructuring-bind
(&key hide name key favorite &allow-other-keys) item
;; hide items explicitly hidden, without key or wrong category.
(if hide
""
(concat
(mu4e--main-action-str
(format "\t* [%s] %s " (format "%c%c" shortcut key)
(propertize
name 'face
(if favorite 'mu4e-header-key-face nil))))
(format "%s%s\n"
(make-string (- max-length (string-width name)) ?\s)
(mu4e--query-item-display-counts item)))))) data ""))
(defun mu4e--key-val (key val &optional unit)
"Show a KEY / VAL pair, with optional UNIT."
@ -255,7 +226,7 @@ character of the keyboard shortcut
(defun mu4e--baseline-time-string ()
"Calculate the baseline time string."
(let* ((baseline-t mu4e--baseline-tstamp)
(let* ((baseline-t mu4e--query-items-baseline-tstamp)
(updated-t (plist-get mu4e-index-update-status :tstamp))
(delta-t (and baseline-t updated-t
(float-time (time-subtract updated-t baseline-t)))))
@ -268,10 +239,12 @@ character of the keyboard shortcut
(defun mu4e--main-redraw-buffer ()
"Redraw the main buffer."
(with-current-buffer mu4e-main-buffer-name
(let ((inhibit-read-only t)
(pos (point))
(addrs (mu4e-personal-addresses))
(max-length (mu4e--longest-of-maildirs-and-bookmarks)))
(let* ((inhibit-read-only t)
(pos (point))
(addrs (mu4e-personal-addresses))
(max-length (seq-reduce (lambda (a b)
(max a (length (plist-get b :name))))
(mu4e-query-items) 0)))
(erase-buffer)
(insert
"* "
@ -281,17 +254,17 @@ character of the keyboard shortcut
"\n\n"
(propertize " Basics\n\n" 'face 'mu4e-title-face)
(mu4e--main-action-str
"\t* [j]ump to some maildir\n" #'mu4e~headers-jump-to-maildir)
"\t* [j]ump to some maildir\n" #'mu4e-search-maildir)
(mu4e--main-action-str
"\t* enter a [s]earch query\n" #'mu4e-search)
(mu4e--main-action-str
"\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) max-length)
(mu4e--main-items (mu4e-query-items 'bookmarks) ?b max-length)
"\n"
(propertize " Maildirs\n\n" 'face 'mu4e-title-face)
(mu4e--main-items ?j (mu4e--maildirs-with-query) max-length)
(mu4e--main-items (mu4e-query-items 'maildirs) ?j max-length)
"\n"
(propertize " Misc\n\n" 'face 'mu4e-title-face)
@ -301,7 +274,8 @@ character of the keyboard shortcut
(mu4e--main-action-str "\t* [U]pdate email & database\n"
#'mu4e-update-mail-and-index)
(mu4e--main-action-str "\t* [R]eset baseline\n" #'mu4e--reset-baseline)
(mu4e--main-action-str "\t* [R]eset baseline\n"
#'mu4e--main-reset-baseline)
;; show the queue functions if `smtpmail-queue-dir' is defined
(if (file-directory-p smtpmail-queue-dir)
@ -318,7 +292,7 @@ character of the keyboard shortcut
(mu4e--key-val "last updated"
(current-time-string
(plist-get mu4e-index-update-status :tstamp)))
(if mu4e--baseline-tstamp
(if mu4e--query-items-baseline-tstamp
(mu4e--key-val "baseline" (mu4e--baseline-time-string))
"")
(mu4e--key-val "database-path" (mu4e-database-path))
@ -369,27 +343,23 @@ character of the keyboard shortcut
(declare-function mu4e--start "mu4e")
(defun mu4e--main-view (&optional refresh no-reset)
"Create or refresh the mu4e main-view, and switch to it.
When REFRESH is non nil refresh infos from server.
(defun mu4e--main-view ()
"(Re)create the mu4e main-view, and switch to it.
If `mu4e-split-view' equals \='single-window, show a mu4e menu
instead."
(unless no-reset
(mu4e--reset-baseline))
(if (eq mu4e-split-view 'single-window)
(mu4e--main-menu)
(let ((buf (get-buffer-create mu4e-main-buffer-name))
(inhibit-read-only t))
;; `mu4e--main-view' is called from `mu4e--start', so don't call it
;; a second time here i.e. do not refresh unless specified
;; explicitly with REFRESH arg.
;; `mu4e--main-view' is called from `mu4e--start', so don't call it a
;; second time here i.e. do not refresh unless specified explicitly with
;; REFRESH arg.
(with-current-buffer buf
(if refresh
(mu4e--start 'mu4e--main-redraw-buffer)
(mu4e--main-redraw-buffer)))
(mu4e-display-buffer buf t)
(goto-char (point-min)))))
(mu4e--main-redraw-buffer))
(mu4e-display-buffer buf t)))
(goto-char (point-min)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactive functions

View File

@ -63,11 +63,6 @@ the buffer-local one."
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
"Minor mode for showing mu4e information on the modeline."
;; This is a bit special 'global' mode, since it consists of both
@ -87,6 +82,13 @@ the buffer-local one."
global-mode-string)))
(force-mode-line-update)))
(defun mu4e--modeline-update ()
"Recalculate and force-update the modeline."
(when mu4e-modeline-mode
(setq mu4e--modeline-string-cached nil)
(force-mode-line-update)))
(provide 'mu4e-modeline)
;;; mu4e-modeline.el ends here

221
mu4e/mu4e-query-items.el Normal file
View File

@ -0,0 +1,221 @@
;;; mu4e-query-items.el -- part of mu4e -*- lexical-binding: t -*-
;; Copyright (C) 2023 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; This file is not part of GNU Emacs.
;; mu4e is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
;; (at your option) any later version.
;; mu4e is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with mu4e. If not, see <http://www.gnu.org/licenses/>.
;;; Commentary:
;;
;; Managing the last query results / baseline, which we use to get the
;; unread-counts, i.e., query items. `mu4e-query-items` delivers these items,
;; aggregated from various sources.
;;; Code:
;;; Last & baseline query results for bookmarks.
(require 'cl-lib)
(require 'mu4e-helpers)
(require 'mu4e-server)
(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
the latest query-items.")
(defvar mu4e--query-items-baseline-tstamp nil
"Timestamp for when the query-items baseline was updated.")
(defun mu4e--bookmark-query (bm)
"Get query string for some bookmark BM."
(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--query-items-pick-favorite (items)
"Pick the :favorite querty item.
If ITEMS does not yet have a favorite item, pick the first."
(unless (seq-find
(lambda (item) (plist-get item :favorite)) items)
(plist-put (car items) :favorite t))
items)
(defvar mu4e--bookmark-items-cached nil "Cached bookmarks query items.")
(defvar mu4e--maildir-items-cached nil "Cached maildirs query items.")
(declare-function mu4e-bookmarks "mu4e-bookmarks")
(declare-function mu4e-maildir-shortcuts "mu4e-folders")
(defun mu4e--query-items-reset ()
"Reset the query items."
(setq mu4e--bookmark-items-cached nil
mu4e--maildir-items-cached nil)
(run-hooks 'mu4e-query-items-updated-hook))
(defun mu4e--query-items-reset-baseline ()
"Reset the baseline query-items."
(setq mu4e--query-items-baseline (mu4e-server-query-items)
mu4e--query-items-baseline-tstamp (current-time))
(mu4e--query-items-reset))
(defun mu4e--query-item-display-counts (item)
"Get the count display string for some query-data ITEM."
;; 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
&allow-other-keys) item
(if hide-unread
""
(concat
(propertize (number-to-string unread)
'face 'mu4e-header-key-face
'help-echo "Number of unread")
(if (<= delta-unread 0) ""
(propertize (format "(%+d)" delta-unread) 'face
'mu4e-unread-face))
"/"
(propertize (number-to-string count)
'help-echo "Total number")))))
(defun mu4e--query-items-refresh()
"Get the latest query data from the mu4e server."
(mu4e--server-queries
(mapcar #'mu4e--bookmark-query
(seq-filter (lambda (item)
(and (not (or (plist-get item :hide)
(plist-get item :hide-unread)))))
(mu4e-query-items)))))
(defun mu4e--query-items-queries-handler (_sexp)
"Handler for queries responses from the mu4e-server.
I.e. what we get in response to mu4e--query-items-refresh."
;; if we don't have a baseline yet, set it. (note that
;; mu4e--query-items-reset-baseline also calls mu4e--query-items-reset.
(if (not mu4e--query-items-baseline)
(progn
(mu4e--query-items-reset-baseline))
(mu4e--query-items-reset))
;; for side-effects; recalculate.
(mu4e-query-items))
;; this makes for O(n*m)... but with typically small(ish) n,m. Perhaps use a
;; hash for last-query-items and baseline-results?
(defun mu4e--query-find-item (query data)
"Find the item in DATA for the given QUERY."
(seq-find (lambda (item)
(equal query (mu4e--bookmark-query item)))
data))
(defun mu4e--make-query-items (data type)
"Map the items in DATA to plists with aggregated query information.
DATA is either the bookmarks or maildirs (user-defined).
LAST-RESULTS-DATA contains unread/counts we received from the
server, while BASELINE-DATA contains the same but taken at some
earier time.
The TYPE denotes the category for the query item, a symbol
bookmark or maildir."
(seq-map
(lambda (item)
(let* ((maildir (plist-get item :maildir))
;; for maildirs, construct the query
(query (if (equal type 'maildirs)
(format "maildir:\"%s\"" maildir)
(plist-get item :query)))
(name (plist-get item :name))
;; maildir items may have an implicit name
;; which is the maildir value.
(name (or name (and (equal type 'maildirs) maildir)))
(last-results (mu4e-server-query-items))
(baseline mu4e--query-items-baseline)
(baseline-item (mu4e--query-find-item query baseline))
(last-results-item (mu4e--query-find-item query last-results))
(count (or (plist-get last-results-item :count) 0))
(unread (or (plist-get last-results-item :unread) 0))
(baseline-count (or (plist-get baseline-item :count) count))
(baseline-unread (or (plist-get baseline-item :unread) unread))
(delta-unread (- unread baseline-unread))
(value
(list
:name name
:query query
:key (plist-get item :key)
:count count
:unread unread
:delta-count (- count baseline-count)
:delta-unread delta-unread)))
;; nil props bring me discomfort
(when (plist-get item :favorite)
(plist-put value :favorite t))
(when (plist-get item :hide)
(plist-put value :hide t))
(when (plist-get item :hide-unread)
(plist-put value :hide-unread t))
value))
data))
;; Note: uipdating is lazy, only happens with the first caller to
;; mu4e-query items.
(defvar mu4e-query-items-updated-hook nil
"Hook run when the query items have been updated.")
(defun mu4e-query-items (&optional type)
"Grab query items of TYPE.
TYPE is symbol; either bookmarks or maildirs, or nil for both.
This combines:
- the latest queries data (i.e., `(mu4e-server-query-items)')
- baseline queries data (i.e. `mu4e-baseline')
with the combined queries for `(mu4e-bookmarks)' and
`(mu4e-maildir-shortcuts)' in bookmarks-compatible plists.
This packages the aggregated information in a format that is convenient
for use in various places."
(cond
((equal type 'bookmarks)
(or mu4e--bookmark-items-cached
(setq mu4e--bookmark-items-cached
(mu4e--query-items-pick-favorite
(mu4e--make-query-items (mu4e-bookmarks) 'bookmarks)))))
((equal type 'maildirs)
(or mu4e--maildir-items-cached
(setq mu4e--maildir-items-cached
(mu4e--make-query-items (mu4e-maildir-shortcuts) 'maildirs))))
((not type)
(append (mu4e-query-items 'bookmarks)
(mu4e-query-items 'maildirs)))
(t
(mu4e-error "No such type %s" type))))
(provide 'mu4e-query-items)
;;; mu4e-query-data.el ends here

View File

@ -33,6 +33,7 @@
(require 'mu4e-contacts)
(require 'mu4e-lists)
(require 'mu4e-mark)
(require 'mu4e-query-items)
;;; Configuration
@ -219,11 +220,12 @@ the search."
(interactive)
(let* ((expr
(or expr
(mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: "))))
(fav (mu4e--bookmark-query (mu4e-favorite-bookmark))))
;; reset baseline when searching for bookmark query
(mu4e-ask-bookmark
(if edit "Select bookmark: " "Bookmark: "))))
(fav (mu4e--bookmark-query (mu4e-bookmark-favorite))))
;; reset baseline when searching for the favorite bookmark query
(when (and fav (string= fav expr))
(mu4e--reset-baseline))
(mu4e--query-items-reset-baseline))
(run-hook-with-args 'mu4e-search-bookmark-hook expr)
(mu4e-search expr (when edit "Edit bookmark: ") edit)))

View File

@ -166,18 +166,15 @@ sexp received from the server process.")
(plist-get mu4e--server-props :version))
(mu4e-error "Version unknown; did you start mu4e?")))
;;
;; server-query-results are the results from the counting-queries
;; we do for bookmarks etc. to populate the main view with unread
;; counts.
;;; remember queries result.
(defvar mu4e--server-query-results nil
"Metadata we receive from the mu4e server.")
(defvar mu4e--server-query-items nil
"Query items results we receive from the mu4e server.
Those are the results from the counting-queries
for bookmarks and maildirs.")
(defun mu4e-server-query-results ()
"Get the latest server queries list."
mu4e--server-query-results)
(defun mu4e-server-query-items ()
"Get the latest server query items."
mu4e--server-query-items)
;;; Handling raw server data
@ -333,7 +330,7 @@ The server output is as follows:
;; receive queries info
((plist-get sexp :queries)
(setq mu4e--server-query-results (plist-get sexp :queries))
(setq mu4e--server-query-items (plist-get sexp :queries))
(funcall mu4e-queries-func sexp))
;; received a contacts message

View File

@ -82,12 +82,8 @@ is non-nil."
(interactive "P")
;; start mu4e, then show the main view
(mu4e--init-handlers)
;; i.e., only auto update baseline when user explicitly requested
(when (or (not mu4e--baseline-tstamp)
(and (not background) (called-interactively-p 'interactive)))
(mu4e--reset-baseline))
(mu4e--modeline-update)
(mu4e--start (unless background 'mu4e--main-view)))
(mu4e--start (unless background #'mu4e--main-view))
(mu4e--query-items-refresh))
(defun mu4e-quit()
"Quit the mu4e session."
@ -138,34 +134,12 @@ Invoke FUNC if non-nil."
(lambda () (mu4e-update-mail-and-index
mu4e-index-update-in-background)))))))
(defun mu4e--server-bookmarks-queries()
(mu4e--server-queries
(mapcar ;; send it a list of queries we'd like to see read/unread info for
(lambda (bm)
(funcall (or mu4e-query-rewrite-function #'identity)
(mu4e--bookmark-query bm)))
;; exclude bookmarks with certain flags
(seq-filter (lambda (bm)
(and (not (or (plist-get bm :hide)
(plist-get bm :hide-unread)))))
(append (mu4e-bookmarks)
(mu4e--maildirs-with-query))))))
(defun mu4e--queries-handler (_sexp)
;; we've received new server-queries; so update the main view
;; (if any) and the modeline.
;; 1. update the query results (i.e. process the new server queries)
(mu4e-last-query-results 'refresh)
(if mu4e--baseline
(mu4e-last-query-results 'refresh)
(mu4e--reset-baseline))
;; note: mu4e-reset-baseline implies mu4e--last-query-results
;; 2. update the main view, if any
(when (buffer-live-p mu4e-main-buffer-name)
(with-current-buffer mu4e-main-buffer-name
(revert-buffer))))
;; ;; 2. update the main view, if any
;; (when-let ((mainbuf (get-buffer mu4e-main-buffer-name)))
;; (when (buffer-live-p mainbuf)
;; (mu4e--main-redraw-buffer)))
;; ;; 3. modeline.
;; (mu4e--modeline-update)
(defun mu4e--start (&optional func)
"Start mu4e.
@ -177,10 +151,14 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
FUNC (if non-nil) afterwards."
(unless (mu4e-context-current)
(mu4e--context-autoswitch nil mu4e-context-policy))
(setq mu4e-pong-func (lambda (info) (mu4e--pong-handler info func)))
(setq mu4e-pong-func
(lambda (info) (mu4e--pong-handler info func)))
(mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global)
(when mu4e-modeline-support
(mu4e-modeline-mode)
(add-hook 'mu4e-query-items-updated-hook
#'mu4e--modeline-update))
(mu4e-modeline-mode (if mu4e-modeline-support 1 -1))
(mu4e--server-bookmarks-queries)
(mu4e--server-ping)
;; maybe request the list of contacts, automatically refreshed after
;; reindexing
@ -192,7 +170,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(cancel-timer mu4e--update-timer)
(setq mu4e--update-timer nil))
(mu4e-clear-caches)
(remove-hook 'mu4e-query-items-updated-hook
#'mu4e--modeline-update)
(mu4e-modeline-mode -1)
(mu4e--server-kill)
;; kill all mu4e buffers
@ -251,7 +230,7 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(if mu4e-index-lazy-check "Lazy indexing" "Indexing")
checked updated cleaned-up)
;; index done; grab updated queries
(mu4e--server-bookmarks-queries)
(mu4e--query-items-refresh)
(run-hooks 'mu4e-index-updated-hook)
;; backward compatibility...
(unless (zerop (+ updated cleaned-up))
@ -262,7 +241,7 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(when (and (buffer-live-p mainbuf) (get-buffer-window mainbuf))
(save-window-excursion
(select-window (get-buffer-window mainbuf))
(mu4e--main-view 'refresh 'no-reset))))))
(mu4e--main-view))))))
((plist-get info :message)
(mu4e-index-message "%s" (plist-get info :message))))))
@ -283,7 +262,7 @@ chance."
(mu4e-setq-if-nil mu4e-contacts-func #'mu4e--update-contacts)
(mu4e-setq-if-nil mu4e-info-func #'mu4e--info-handler)
(mu4e-setq-if-nil mu4e-pong-func #'mu4e--default-handler)
(mu4e-setq-if-nil mu4e-queries-func #'mu4e--queries-handler))
(mu4e-setq-if-nil mu4e-queries-func #'mu4e--query-items-queries-handler))
(defun mu4e-clear-caches ()
"Clear any cached resources."