From 49e094531c3296708b9eb513ed7ba306fd6eba7a Mon Sep 17 00:00:00 2001 From: "Dirk-Jan C. Binnema" Date: Sat, 7 Jan 2023 15:11:10 +0200 Subject: [PATCH] mu4e: add support for :favorite bookmark Show favorite bookmark in main-screen (highlighted) Add baseline-reset when main screen is shown or when the favority bookmark is searched. Some code cleanup --- mu4e/mu4e-context.el | 6 +- mu4e/mu4e-main.el | 131 ++++++++++++++++++------------------------ mu4e/mu4e-obsolete.el | 2 + mu4e/mu4e-search.el | 12 ++-- mu4e/mu4e-update.el | 1 - mu4e/mu4e.el | 55 ++++++++++-------- 6 files changed, 100 insertions(+), 107 deletions(-) diff --git a/mu4e/mu4e-context.el b/mu4e/mu4e-context.el index 0858db8e..6adf97f1 100644 --- a/mu4e/mu4e-context.el +++ b/mu4e/mu4e-context.el @@ -146,8 +146,7 @@ non-nil." (setq mu4e--context-current context) (run-hooks 'mu4e-context-changed-hook) - (mu4e-message "Switched context to %s" (mu4e-context-name context)) - (mu4e--modeline-update)) + (mu4e-message "Switched context to %s" (mu4e-context-name context))) context)) (defun mu4e--context-autoswitch (&optional msg policy) @@ -225,8 +224,7 @@ An empty string \"\" if there is none." (define-key map (kbd ";") #'mu4e-context-switch) map) :lighter "" - (mu4e--modeline-register #'mu4e--context-modeline-item) - (mu4e--modeline-update)) + (mu4e--modeline-register #'mu4e--context-modeline-item)) ;;; (provide 'mu4e-context) diff --git a/mu4e/mu4e-main.el b/mu4e/mu4e-main.el index 5ee82b44..8462462b 100644 --- a/mu4e/mu4e-main.el +++ b/mu4e/mu4e-main.el @@ -1,6 +1,6 @@ ;;; mu4e-main.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- -;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema +;; Copyright (C) 2011-2023 Dirk-Jan C. Binnema ;; Author: Dirk-Jan C. Binnema ;; Maintainer: Dirk-Jan C. Binnema @@ -24,9 +24,9 @@ ;;; Code: -(require 'smtpmail) ;; the queueing stuff (silence elint) -(require 'mu4e-helpers) ;; utility functions -(require 'mu4e-context) ;; the context +(require 'smtpmail) +(require 'mu4e-helpers) +(require 'mu4e-context) (require 'mu4e-bookmarks) (require 'mu4e-folders) (require 'mu4e-update) @@ -58,18 +58,6 @@ the personal addresses." :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 (define-derived-mode mu4e-org-mode org-mode "mu4e:org" @@ -99,11 +87,11 @@ swiching to the main-view (using the `mu4e' command." (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'. +(defun mu4e--main-reset-baseline() + "Main-view version of `mu4e-reset-query-results'. This version handles updating the current screen as well." (interactive) - (mu4e-reset-baseline-query-results) + (mu4e--reset-baseline) (revert-buffer)) (defvar mu4e-main-mode-map @@ -117,7 +105,7 @@ This version handles updating the current screen as well." (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 "R" #'mu4e--main-reset-baseline) (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) @@ -142,15 +130,16 @@ This version handles updating the current screen as well." (mu4e-context-minor-mode) (mu4e-search-minor-mode) (mu4e-update-minor-mode) - (set (make-local-variable 'revert-buffer-function) #'mu4e--main-view-real) - (add-hook 'mu4e-index-updated-hook #'mu4e--main-update-after-index)) + (setq-local revert-buffer-function + (lambda (_ignore-auto _noconfirm) + (mu4e--main-view 'refresh)))) (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 when STR is clicked (using RET or mouse-2); if FUNC-OR-SHORTCUT is a string, execute the corresponding keyboard action when it is -clicked." +clicked. If HIGHLIGHT is non-nil, hightlight the name." (let ((newstr (replace-regexp-in-string "\\[\\(..?\\)\\]" @@ -175,8 +164,7 @@ clicked." (if (stringp func-or-shortcut) (length newstr) (- (length newstr) 1)) - 'mouse-face 'highlight newstr) - newstr)) + 'mouse-face 'highlight newstr) newstr)) (defun mu4e--longest-of-maildirs-and-bookmarks () "Return the length of longest name of bookmarks and maildirs." @@ -194,7 +182,11 @@ clicked." (used for alignment)." (concat (mu4e--main-action-str - (concat "\t* [" fullkey "] " name) fullkey) + (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)) @@ -203,32 +195,30 @@ clicked." (baseline-unread (or (when baseline (plist-get baseline :unread)) unread)) (delta (- unread baseline-unread))) - (format"%s (%s%s/%s)" + (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") - "") + (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) - "Display the entries for the bookmark/maildir menu. + "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)'. +- 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) - (plist-get item :query)) + (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)) @@ -246,22 +236,17 @@ character of the keyboard shortcut (propertize (concat " " unit) 'face 'mu4e-header-title-face) "") "\n")) -;; NEW This is the old `mu4e--main-view' function but without -;; buffer switching at the end. -(defun mu4e--main-view-real (_ignore-auto _noconfirm) - "The revert buffer function for `mu4e-main-mode'." - (mu4e--main-view-real-1 'refresh)) - -(declare-function mu4e--start "mu4e") - -(defun mu4e--main-view-real-1 (&optional refresh) - "Create `mu4e-main-buffer-name' and set it up. -When REFRESH is non nil refresh infos from server." - (let ((inhibit-read-only t)) - ;; Maybe refresh infos from server. - (if refresh - (mu4e--start 'mu4e--main-redraw-buffer) - (mu4e--main-redraw-buffer)))) +(defun mu4e--baseline-time-string () + "Calculate the baseline time string." + (let* ((baseline-t mu4e--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))))) + (if (and delta-t (> delta-t 0)) + (format-seconds "%Y %D %H %M %z%S ago" delta-t) + (if baseline-t + (current-time-string baseline-t) + "Never")))) (defun mu4e--main-redraw-buffer () "Redraw the main buffer." @@ -276,7 +261,7 @@ When REFRESH is non nil refresh infos from server." (propertize "mu4e" 'face 'mu4e-header-key-face) (propertize " - mu for emacs version " 'face 'mu4e-title-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face) - "\n\n" + "\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) @@ -294,13 +279,12 @@ When REFRESH is non nil refresh infos from server." (propertize " Misc\n\n" 'face 'mu4e-title-face) (mu4e--main-action-str "\t* [;]Switch context\n" - (lambda()(interactive) - (mu4e-context-switch)(revert-buffer))) + (lambda()(interactive) + (mu4e-context-switch)(revert-buffer))) (mu4e--main-action-str "\t* [U]pdate email & database\n" - #'mu4e-update-mail-and-index) - (mu4e--main-action-str "\t* [R]eset query-results baseline\n" - #'mu4e--main-reset-baseline-query-results) + #'mu4e-update-mail-and-index) + (mu4e--main-action-str "\t* [R]eset baseline\n" #'mu4e--reset-baseline) ;; show the queue functions if `smtpmail-queue-dir' is defined (if (file-directory-p smtpmail-queue-dir) @@ -317,19 +301,17 @@ 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)) + (if mu4e--baseline-tstamp + (mu4e--key-val "baseline" (mu4e--baseline-time-string)) "") (mu4e--key-val "database-path" (mu4e-database-path)) (mu4e--key-val "maildir" (mu4e-root-maildir)) (mu4e--key-val "in store" - (format "%d" (plist-get mu4e--server-props :doccount)) - "messages") + (format "%d" (plist-get mu4e--server-props :doccount)) + "messages") (if mu4e-main-hide-personal-addresses "" (mu4e--key-val "personal addresses" - (if addrs (mapconcat #'identity addrs ", " ) "none")))) + (if addrs (mapconcat #'identity addrs ", " ) "none")))) (if mu4e-main-hide-personal-addresses "" (unless (mu4e-personal-address-p user-mail-address) @@ -368,26 +350,31 @@ When REFRESH is non nil refresh infos from server." (count-lines (point-min) (point-max))) (error 0))) +(declare-function mu4e--start "mu4e") + (defun mu4e--main-view (&optional refresh) - "Create the mu4e main-view, and switch to it or show the menu. + "Create or refresh the mu4e main-view, and switch to it. When REFRESH is non nil refresh infos from server. If `mu4e-split-view' equals \='single-window, show a mu4e menu instead." + (mu4e--reset-baseline) (if (eq mu4e-split-view 'single-window) (mu4e--main-menu) - (let ((buf (get-buffer-create mu4e-main-buffer-name))) + (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. (with-current-buffer buf - (mu4e--main-view-real-1 refresh)) + (if refresh + (mu4e--start 'mu4e--main-redraw-buffer) + (mu4e--main-redraw-buffer))) (mu4e-display-buffer buf t) (goto-char (point-min))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Interactive functions -;; NEW ;; Toggle mail sending mode without switching (defun mu4e--main-toggle-mail-sending-mode () "Toggle sending mail mode, either queued or direct." @@ -409,7 +396,7 @@ instead." '(("jump" . mu4e~headers-jump-to-maildir) ("search" . mu4e-search) ("Compose" . mu4e-compose-new) - ("bookmarks" . mu4e-headers-search-bookmark) + ("bookmarks" . mu4e-search-bookmark) (";Switch context" . mu4e-context-switch) ("Update" . mu4e-update-mail-and-index) ("News" . mu4e-news) @@ -420,11 +407,5 @@ instead." (sit-for 1) (mu4e--main-menu)))) -(defun mu4e--main-update-after-index () - "Update the main view buffer after indexing." - (when (buffer-live-p mu4e-main-buffer-name) - (with-current-buffer mu4e-main-buffer-name - (revert-buffer)))) - (provide 'mu4e-main) ;;; mu4e-main.el ends here diff --git a/mu4e/mu4e-obsolete.el b/mu4e/mu4e-obsolete.el index 1b299177..529f3df1 100644 --- a/mu4e/mu4e-obsolete.el +++ b/mu4e/mu4e-obsolete.el @@ -148,6 +148,8 @@ (define-obsolete-function-alias 'mu4e-read-query 'mu4e-search-read-query "1.7.0") +(make-obsolete-variable 'mu4e-display-update-status-in-modeline + "No longer used" "1.9.11") ;; mu4e-headers (make-obsolete-variable 'mu4e-headers-field-properties-function diff --git a/mu4e/mu4e-search.el b/mu4e/mu4e-search.el index 53a3443d..9c56c0b1 100644 --- a/mu4e/mu4e-search.el +++ b/mu4e/mu4e-search.el @@ -165,8 +165,7 @@ but also manually invoked searches." ;;; History (defvar mu4e--search-query-past nil "Stack of queries before the present one.") -(defvar mu4e--search-query-future nil - "Stack of queries after the present one.") +(defvar mu4e--search-query-future nil "Stack of queries after the present one.") (defvar mu4e--search-query-stack-size 20 "Maximum size for the query stacks.") (defvar mu4e--search-last-query nil @@ -217,9 +216,14 @@ show the message with MSGID." If EDIT is non-nil, let the user edit the bookmark before starting the search." (interactive) - (let ((expr + (let* ((expr (or expr - (mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: "))))) + (mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: ")))) + (fav (mu4e--bookmark-query (mu4e-favorite-bookmark)))) + ;; reset baseline when searching for bookmark query + (when (and fav (string= fav expr)) + (mu4e--reset-baseline)) + (run-hook-with-args 'mu4e-search-bookmark-hook expr) (mu4e-search expr (when edit "Edit bookmark: ") edit))) diff --git a/mu4e/mu4e-update.el b/mu4e/mu4e-update.el index c38c8fcb..accb7849 100644 --- a/mu4e/mu4e-update.el +++ b/mu4e/mu4e-update.el @@ -311,7 +311,6 @@ 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) diff --git a/mu4e/mu4e.el b/mu4e/mu4e.el index 86a85fe4..0abb1156 100644 --- a/mu4e/mu4e.el +++ b/mu4e/mu4e.el @@ -73,6 +73,7 @@ (with-eval-after-load 'desktop (eval '(add-to-list 'desktop-modes-not-to-save 'mu4e-compose-mode))) + ;;;###autoload (defun mu4e (&optional background) "If mu4e is not running yet, start it. @@ -81,12 +82,11 @@ 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 (and mu4e-main-auto-reset-baseline - (not background) (called-interactively-p 'interactive)) - (mu4e-reset-baseline-query-results)) - + (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))) (defun mu4e-quit() @@ -138,6 +138,19 @@ 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. @@ -164,18 +177,10 @@ Otherwise, check requirements, then start mu4e. When successful, invoke (unless (mu4e-context-current) (mu4e--context-autoswitch nil mu4e-context-policy)) (setq mu4e-pong-func (lambda (info) (mu4e--pong-handler info func))) - (mu4e--server-ping - (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) - (plist-get bm :query))) - ;; exclude bookmarks that are not strings, and with certain flags - (seq-filter (lambda (bm) - (and (stringp (plist-get bm :query)) - (not (or (plist-get bm :hide) - (plist-get bm :hide-unread))))) - (append (mu4e-bookmarks) - (mu4e--maildirs-with-query))))) + (mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global) + (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 (unless mu4e--contacts-set (mu4e--request-contacts-maybe))) @@ -186,6 +191,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke (cancel-timer mu4e--update-timer) (setq mu4e--update-timer nil)) (mu4e-clear-caches) + + (mu4e-modeline-mode -1) (mu4e--server-kill) ;; kill all mu4e buffers (mapc @@ -205,7 +212,7 @@ Otherwise, check requirements, then start mu4e. When successful, invoke ;;; Handlers (defun mu4e--default-handler (&rest args) "Dummy handler function with arbitrary ARGS." - (mu4e-error "Not handled: %S" args)) + (mu4e-error "Not handled: %s" args)) (defun mu4e--error-handler (errcode errmsg) "Handler function for showing an error with ERRCODE and ERRMSG." @@ -242,6 +249,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke "%s completed; checked %d, updated %d, cleaned-up %d" (if mu4e-index-lazy-check "Lazy indexing" "Indexing") checked updated cleaned-up) + ;; index done; grab updated queries + (mu4e--server-bookmarks-queries) (run-hooks 'mu4e-index-updated-hook) ;; backward compatibility... (unless (zerop (+ updated cleaned-up)) @@ -268,11 +277,11 @@ chance." (mu4e-setq-if-nil mu4e-found-func #'mu4e~headers-found-handler) (mu4e-setq-if-nil mu4e-erase-func #'mu4e~headers-clear) - (mu4e-setq-if-nil mu4e-sent-func #'mu4e--default-handler) - (mu4e-setq-if-nil mu4e-compose-func #'mu4e~compose-handler) - (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-sent-func #'mu4e--default-handler) + (mu4e-setq-if-nil mu4e-compose-func #'mu4e~compose-handler) + (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)) (defun mu4e-clear-caches ()