Merge branch 'wip/djcb/modeline'

This commit is contained in:
Dirk-Jan C. Binnema 2023-01-07 15:43:26 +02:00
commit 54dc5cfabe
16 changed files with 794 additions and 507 deletions

View File

@ -41,10 +41,19 @@
manual for details. manual for details.
- One change is that ~mu4e-split-view~ can no longer be a function; the new - One change is that ~mu4e-split-view~ can no longer be a function; the new
way is to use ~display-buffer-alist~ as explained in the [[info:mu4e:Buffer Display][manual]]. way is to use ~display-buffer-alist~ as explained in the [[info:mu4e:Buffer Display][manual]]
- ~mu4e~ now keeps track of the 'baseline' query results and shows the - ~mu4e~ now keeps track of 'baseline' query results and shows the difference
difference from that in the main view. See the [[info:mu4e#Bookmarks and Maildirs][manual entry]] for details. from that in the main view and modeline. See the [[info:mu4e#Bookmarks and Maildirs][manual entry]] for details.
- Related to that, you can now crown one of your bookmarks in =mu4e-bookmarks=
with ~:monitor t~, causing it to be highlighted in the main view and used in
the mode-line. See the new [[info:mu4e#Modeline][modeline entry]] in the manual; this uses the new
=mu4e-modeline-mode= minor-mode.
- Bookmark ~:query~ values must be strings now; earlier version half-attempted
to allow for functions to works as well, but it didn't work. So now we
officially remove this.
- when moving messages (which includes changing flags), file-flags changes - when moving messages (which includes changing flags), file-flags changes
are propagated to duplicates of the messages; that is, e.g. the /Seen/ or are propagated to duplicates of the messages; that is, e.g. the /Seen/ or
@ -81,7 +90,7 @@
further parameters ~mu~ shows both subcommands and scripts. This is a further parameters ~mu~ shows both subcommands and scripts. This is a
work-in-progress! work-in-progress!
- The per-(week|day|year|year-month) script have been combined into a - The per-(week|day|year|year-month) scripts have been combined into a
~histogram~ script. If you have Guile-support enabled, and have ~gnuplot~ ~histogram~ script. If you have Guile-support enabled, and have ~gnuplot~
installed, you can do e.g., installed, you can do e.g.,
@ -92,7 +101,7 @@
to get a histogram of such messages. Note, this area is under active to get a histogram of such messages. Note, this area is under active
developement and will likely change. developement and will likely change.
*** building *** building and installation
- the autotools build (which was deprecated since 1.8) has now been removed. - the autotools build (which was deprecated since 1.8) has now been removed.
we thank it for its services since 2008. We continue with ~meson~. we thank it for its services since 2008. We continue with ~meson~.
@ -339,8 +348,12 @@
out on the maling list. out on the maling list.
* Old news
:PROPERTIES:
:VISIBILITY: folded
:END:
* 1.6 (released, as of July 27 2021) ** 1.6 (released, as of July 27 2021)
NOTE: After upgrading, you need to call ~mu init~, with your prefered parameters NOTE: After upgrading, you need to call ~mu init~, with your prefered parameters
before you can use ~mu~ / ~mu4e~. This is because the underlying database-schema before you can use ~mu~ / ~mu4e~. This is because the underlying database-schema
@ -426,10 +439,7 @@
- Switch the context for existing draft messages using - Switch the context for existing draft messages using
~mu4e-compose-context-switch~ or ~C-c C-;~ in ~mu4e-compose-mode~. ~mu4e-compose-context-switch~ or ~C-c C-;~ in ~mu4e-compose-mode~.
* Old news
:PROPERTIES:
:VISIBILITY: folded
:END:
** 1.4 (released, as of April 18 2020) ** 1.4 (released, as of April 18 2020)
*** mu *** mu
@ -939,7 +949,7 @@ End of search results
- org: improve template keywords - org: improve template keywords
- rework URL handling - rework URL handling
** Release 0.9.5 ** Release 0.9.10
*** mu *** mu

View File

@ -98,6 +98,7 @@ struct Server::Private {
void move_handler(const Command& cmd); void move_handler(const Command& cmd);
void mkdir_handler(const Command& cmd); void mkdir_handler(const Command& cmd);
void ping_handler(const Command& cmd); void ping_handler(const Command& cmd);
void queries_handler(const Command& cmd);
void quit_handler(const Command& cmd); void quit_handler(const Command& cmd);
void remove_handler(const Command& cmd); void remove_handler(const Command& cmd);
void sent_handler(const Command& cmd); void sent_handler(const Command& cmd);
@ -287,17 +288,20 @@ Server::Private::make_command_map()
[&](const auto& params) { mkdir_handler(params); }}); [&](const auto& params) { mkdir_handler(params); }});
cmap.emplace( cmap.emplace(
"ping", "ping",
CommandInfo{
ArgMap{},
"ping the mu-server and get server information in the response",
[&](const auto& params) { ping_handler(params); }});
cmap.emplace(
"queries",
CommandInfo{ CommandInfo{
ArgMap{ ArgMap{
{":queries", {":queries",
ArgInfo{Type::List, false, "queries for which to get read/unread numbers"}}, ArgInfo{Type::List, false, "queries for which to get read/unread numbers"}},
{":skip-dups",
ArgInfo{Type::Symbol,
false,
"whether to exclude messages with duplicate message-ids"}},
}, },
"ping the mu-server and get information in response", "get unread/totals information for a list of queries",
[&](const auto& params) { ping_handler(params); }}); [&](const auto& params) { queries_handler(params); }});
cmap.emplace("quit", CommandInfo{{}, "quit the mu server", [&](const auto& params) { cmap.emplace("quit", CommandInfo{{}, "quit the mu server", [&](const auto& params) {
quit_handler(params); quit_handler(params);
@ -916,9 +920,28 @@ Server::Private::ping_handler(const Command& cmd)
const auto storecount{store().size()}; const auto storecount{store().size()};
if (storecount == (unsigned)-1) if (storecount == (unsigned)-1)
throw Error{Error::Code::Store, "failed to read store"}; throw Error{Error::Code::Store, "failed to read store"};
Sexp addrs;
for (auto&& addr : store().properties().personal_addresses)
addrs.add(addr);
output_sexp(Sexp()
.put_props(":pong", "mu")
.put_props(":props",
Sexp().put_props(
":version", VERSION,
":personal-addresses", std::move(addrs),
":database-path", store().properties().database_path,
":root-maildir", store().properties().root_maildir,
":doccount", storecount)));
}
void
Server::Private::queries_handler(const Command& cmd)
{
const auto queries{cmd.string_vec_arg(":queries") const auto queries{cmd.string_vec_arg(":queries")
.value_or(std::vector<std::string>{})}; .value_or(std::vector<std::string>{})};
Sexp qresults; Sexp qresults;
for (auto&& q : queries) { for (auto&& q : queries) {
const auto count{store_.count_query(q)}; const auto count{store_.count_query(q)};
@ -929,22 +952,10 @@ Server::Private::ping_handler(const Command& cmd)
":unread", unread)); ":unread", unread));
} }
Sexp addrs; output_sexp(Sexp(":queries"_sym, std::move(qresults)));
for (auto&& addr : store().properties().personal_addresses)
addrs.add(addr);
auto lst = Sexp().put_props(":pong", "mu");
auto proplst = Sexp().put_props(
":version", VERSION,
":personal-addresses", std::move(addrs),
":database-path", store().properties().database_path,
":root-maildir", store().properties().root_maildir,
":doccount", storecount,
":queries", std::move(qresults));
output_sexp(lst.put_props(":props", std::move(proplst)));
} }
void void
Server::Private::quit_handler(const Command& cmd) Server::Private::quit_handler(const Command& cmd)
{ {

View File

@ -106,8 +106,7 @@ CommandHandler::invoke(const Command& cmd, bool do_validate) const
const auto cmit{cmap_.find(cmd.name())}; const auto cmit{cmap_.find(cmd.name())};
if (cmit == cmap_.cend()) if (cmit == cmap_.cend())
return Err(Error::Code::Command, return Err(Error::Code::Command,
"unknown command in command '%s'", "unknown command '%s'", cmd.to_string().c_str());
cmd.to_string().c_str());
const auto& cmd_info{cmit->second}; const auto& cmd_info{cmit->second};
if (do_validate) { if (do_validate) {

View File

@ -45,6 +45,7 @@ mu4e_srcs=[
'mu4e-main.el', 'mu4e-main.el',
'mu4e-mark.el', 'mu4e-mark.el',
'mu4e-message.el', 'mu4e-message.el',
'mu4e-modeline.el',
'mu4e-obsolete.el', 'mu4e-obsolete.el',
'mu4e-org.el', 'mu4e-org.el',
'mu4e-search.el', 'mu4e-search.el',

View File

@ -1,6 +1,6 @@
;;; mu4e-bookmarks.el -- part of mu4e -*- lexical-binding: t -*- ;;; mu4e-bookmarks.el -- part of mu4e -*- lexical-binding: t -*-
;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2023 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -24,6 +24,8 @@
;;; Code: ;;; Code:
(require 'mu4e-helpers) (require 'mu4e-helpers)
(require 'mu4e-server)
(require 'mu4e-modeline)
;;; Configuration ;;; Configuration
@ -50,19 +52,24 @@
Each of the list elements is a plist with at least: Each of the list elements is a plist with at least:
`:name' - the name of the query `:name' - the name of the query
`:query' - the query expression or function `:query' - the query expression string (not a function)
`:key' - the shortcut key. `:key' - the shortcut key (single character)
Note that the :query parameter can be a function/lambda. Note that the :query parameter can be a function/lambda.
Optionally, you can add the following: `:hide' - if t, the Optionally, you can add the following:
bookmark is hidden from the main-view and speedbar.
`:hide-unread' - do not show the counts of unread/total number of
matches for the query in the main-view. This can be useful if a
bookmark uses a very slow query.
`:hide-unread' is implied from `:hide'. Furthermore, it is - `:favorite' - if t, monitor the results of this query, and make
implied when `:query' is a function. it eligible for showing its status in the emacs modeline. At mose
one bookmark should have this set to t (otherwise the _first_
bookmark is the implicit favorite)
- `:hide' - if t, the bookmark is hidden from the main-view and
speedbar.
- `:hide-unread' - do not show the counts of
unread/total number of matches for the query in the main-view.
This can be useful if a bookmark uses a very slow query.
`:hide-unread' is implied from `:hide'.
Note: for efficiency, queries used to determine the unread/all Note: for efficiency, queries used to determine the unread/all
counts do not discard duplicate or unreadable messages. Thus, the counts do not discard duplicate or unreadable messages. Thus, the
@ -72,7 +79,7 @@ query."
:group 'mu4e-bookmarks) :group 'mu4e-bookmarks)
(defun mu4e-ask-bookmark (prompt) (defun mu4e-ask-bookmark (prompt)
"Ask the user for a bookmark (using PROMPT) as defined in "Ask the user for a bookmark (using PROMPT) as defined in
`mu4e-bookmarks', then return the corresponding query." `mu4e-bookmarks', then return the corresponding query."
(unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined")) (unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined"))
@ -88,23 +95,32 @@ query."
(kar (read-char (concat prompt bmarks)))) (kar (read-char (concat prompt bmarks))))
(mu4e-get-bookmark-query kar))) (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) (defun mu4e-get-bookmark-query (kar)
"Get the corresponding bookmarked query for shortcut KAR. "Get the corresponding bookmarked query for shortcut KAR.
Raise an error if none is found." Raise an error if none is found."
(let* ((chosen-bm (let ((chosen-bm
(or (seq-find (or (seq-find
(lambda (bm) (lambda (bm)
(= kar (plist-get bm :key))) (= kar (plist-get bm :key)))
(mu4e-bookmarks)) (mu4e-bookmarks))
(mu4e-warn "Unknown shortcut '%c'" kar))) (mu4e-warn "Unknown shortcut '%c'" kar))))
(expr (plist-get chosen-bm :query)) (mu4e--bookmark-query chosen-bm)))
(expr (if (not (functionp expr)) expr
(funcall expr)))
(query (eval expr)))
(if (stringp query)
query
(mu4e-warn "Expression must evaluate to query string ('%S')" expr))))
(defun mu4e-bookmark-define (query name key) (defun mu4e-bookmark-define (query name key)
"Define a bookmark for QUERY with NAME and shortcut KEY. "Define a bookmark for QUERY with NAME and shortcut KEY.
@ -116,8 +132,8 @@ with KEY."
(= (plist-get bm :key) key)) (= (plist-get bm :key) key))
(mu4e-bookmarks))) (mu4e-bookmarks)))
(cl-pushnew `(:name ,name (cl-pushnew `(:name ,name
:query ,query :query ,query
:key ,key) :key ,key)
mu4e-bookmarks :test 'equal)) mu4e-bookmarks :test 'equal))
(defun mu4e-bookmarks () (defun mu4e-bookmarks ()
@ -125,9 +141,159 @@ with KEY."
Convert from the old format if needed." Convert from the old format if needed."
(seq-map (lambda (item) (seq-map (lambda (item)
(if (and (listp item) (= (length item) 3)) (if (and (listp item) (= (length item) 3))
`(:name ,(nth 1 item) :query ,(nth 0 item) `(:name ,(nth 1 item) :query ,(nth 0 item)
:key ,(nth 2 item)) :key ,(nth 2 item))
item)) mu4e-bookmarks)) 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 'force)) ; for side-effects
(defvar mu4e--last-query-results-cached nil)
(defun mu4e-last-query-results(&optional force)
"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 its nil or FORCE is non-nil."
(if (and mu4e--last-query-results-cached (not force))
mu4e--last-query-results-cached ;; use cache
;; otherwise, recalculate.
(let* ((favorite (mu4e-favorite-bookmark))
(favorite-query
(and favorite (mu4e--bookmark-query favorite))))
(setq mu4e--last-query-results-cached ;; 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."
(seq-find
(lambda (elm) (string= query (mu4e--bookmark-query elm)))
(mu4e-last-query-results)))
;; for Zero-Inbox afficionados
(defvar mu4e-modeline-all-clear '("C:" . "🌀")
"No more messages at all for this query.")
(defvar mu4e-modeline-all-read '("R:" . "")
"No unread messages left.")
(defvar mu4e-modeline-unread-items '("U:" . "📫")
"There are some unread items.")
(defvar mu4e-modeline-new-items '("N:" . "🔥")
"There are some new items after the baseline.
I.e., very new messages.")
(declare-function mu4e-search-bookmark "mu4e-search")
(defun mu4e-jump-to-favorite ()
"Jump to to the favorite bookmark, if any."
(interactive)
(when-let ((fav (mu4e--bookmark-query (mu4e-favorite-bookmark))))
(mu4e-search-bookmark fav)))
(defvar mu4e--bookmarks-modeline-cached nil)
(defun mu4e--bookmarks-modeline-item ()
"Modeline item showing message counts for the favorite bookmark.
This uses the one special ':favorite' bookmark, and if there is
one, creates a propertized string for display in the modeline."
(or mu4e--bookmarks-modeline-cached
(setq mu4e--bookmarks-modeline-cached
(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)))
(propertize
(format "%s%s%s/%s "
(funcall (if mu4e-use-fancy-chars 'cdr 'car)
(cond
((> delta 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))
'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))))))))
(defun mu4e--modeline-update()
"Update the modeline and redisplay if mu4e-modeline-mode is
active."
(when mu4e-modeline-mode
(setq mu4e--bookmarks-modeline-cached nil) ;; force recalculation
(force-mode-line-update)))
(provide 'mu4e-bookmarks) (provide 'mu4e-bookmarks)
;;; mu4e-bookmarks.el ends here ;;; mu4e-bookmarks.el ends here

View File

@ -1,6 +1,6 @@
;;; mu4e-context.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;;; mu4e-context.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*-
;; Copyright (C) 2015-2022 Dirk-Jan C. Binnema ;; Copyright (C) 2015-2023 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -28,6 +28,7 @@
;;; Code: ;;; Code:
(require 'mu4e-helpers) (require 'mu4e-helpers)
(require 'mu4e-modeline)
;;; Configuration ;;; Configuration
@ -82,14 +83,6 @@ none."
(if ctx (mu4e-context-name ctx) "<none>"))) (if ctx (mu4e-context-name ctx) "<none>")))
ctx)) ctx))
(defun mu4e-context-label ()
"Propertized string with the current context name.
An empty string \"\" if there is none."
(if (mu4e-context-current)
(concat "[" (propertize (mu4e-quote-for-modeline
(mu4e-context-name (mu4e-context-current)))
'face 'mu4e-context-face) "]") ""))
(cl-defstruct mu4e-context (cl-defstruct mu4e-context
"A mu4e context object with the following members: "A mu4e context object with the following members:
- `name': the name of the context, eg. \"Work\" or \"Private\". - `name': the name of the context, eg. \"Work\" or \"Private\".
@ -153,8 +146,7 @@ non-nil."
(setq mu4e--context-current context) (setq mu4e--context-current context)
(run-hooks 'mu4e-context-changed-hook) (run-hooks 'mu4e-context-changed-hook)
(mu4e-message "Switched context to %s" (mu4e-context-name context)) (mu4e-message "Switched context to %s" (mu4e-context-name context)))
(force-mode-line-update))
context)) context))
(defun mu4e--context-autoswitch (&optional msg policy) (defun mu4e--context-autoswitch (&optional msg policy)
@ -204,13 +196,6 @@ as it is."
(mu4e--context-ask-user "Select context: "))) (mu4e--context-ask-user "Select context: ")))
(_ nil)))))) (_ nil))))))
(defun mu4e-context-in-modeline ()
"Display the mu4e-context (if any) in a (buffer-specific)
global-mode-line."
(add-to-list
(make-local-variable 'global-mode-string)
'(:eval (mu4e-context-label))))
(defmacro with-mu4e-context-vars (context &rest body) (defmacro with-mu4e-context-vars (context &rest body)
"Evaluate BODY, with variables let-bound for CONTEXT (if any). "Evaluate BODY, with variables let-bound for CONTEXT (if any).
`funcall'." `funcall'."
@ -221,6 +206,14 @@ global-mode-line."
(mapcar (lambda(cell) (cdr cell)) vars) (mapcar (lambda(cell) (cdr cell)) vars)
(eval ,@body)))) (eval ,@body))))
(defun mu4e--context-modeline-item ()
"Propertized string with the current context name.
An empty string \"\" if there is none."
(if (mu4e-context-current)
(concat "[" (propertize (mu4e-quote-for-modeline
(mu4e-context-name (mu4e-context-current)))
'face 'mu4e-context-face) "] " ) ""))
(define-minor-mode mu4e-context-minor-mode (define-minor-mode mu4e-context-minor-mode
"Mode for switching the mu4e context." "Mode for switching the mu4e context."
:global nil :global nil
@ -231,7 +224,7 @@ global-mode-line."
(define-key map (kbd ";") #'mu4e-context-switch) (define-key map (kbd ";") #'mu4e-context-switch)
map) map)
:lighter "" :lighter ""
(mu4e-context-in-modeline)) (mu4e--modeline-register #'mu4e--context-modeline-item))
;;; ;;;
(provide 'mu4e-context) (provide 'mu4e-context)

View File

@ -1,6 +1,6 @@
;;; mu4e-headers.el -- part of mu4e -*- lexical-binding: t; coding:utf-8 -*- ;;; mu4e-headers.el -- part of mu4e -*- lexical-binding: t; coding:utf-8 -*-
;; Copyright (C) 2011-2022 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2023 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -812,7 +812,7 @@ true, do *not* update the query history stack."
(mu4e--search-push-query mu4e--search-last-query 'past))) (mu4e--search-push-query mu4e--search-last-query 'past)))
(setq mu4e--search-last-query rewritten-expr) (setq mu4e--search-last-query rewritten-expr)
(setq list-buffers-directory rewritten-expr) (setq list-buffers-directory rewritten-expr)
(mu4e~headers-update-mode-line)) (mu4e--modeline-update))
;; when the buffer is already visible, select it; otherwise, ;; when the buffer is already visible, select it; otherwise,
;; switch to it. ;; switch to it.
@ -1178,7 +1178,10 @@ The following specs are supported:
(mu4e-context-minor-mode) (mu4e-context-minor-mode)
(mu4e-update-minor-mode) (mu4e-update-minor-mode)
(mu4e-search-minor-mode) (mu4e-search-minor-mode)
(hl-line-mode 1)) (hl-line-mode 1)
(mu4e--modeline-register #'mu4e~headers-modeline-item)
(mu4e--modeline-update))
;;; Highlighting ;;; Highlighting
@ -1244,7 +1247,7 @@ message plist, or nil if not found."
;;; Queries & searching ;;; Queries & searching
(defvar mu4e~headers-mode-line-label "") (defvar mu4e~headers-mode-line-label "")
(defun mu4e~headers-update-mode-line () (defun mu4e~headers-modeline-item ()
"Update mode-line settings." "Update mode-line settings."
(let* ((flagstr (let* ((flagstr
(mapconcat (mapconcat
@ -1258,30 +1261,8 @@ message plist, or nil if not found."
(,mu4e-search-skip-duplicates (,mu4e-search-skip-duplicates
. ,mu4e-headers-skip-duplicates-label) . ,mu4e-headers-skip-duplicates-label)
(,mu4e-search-hide-enabled . ,mu4e-headers-hide-label)) (,mu4e-search-hide-enabled . ,mu4e-headers-hide-label))
"")) "")))
(name "mu4e-headers")) (concat flagstr " " mu4e--search-last-query)))
(setq mode-name name)
(setq mu4e~headers-mode-line-label
(concat flagstr " " mu4e--search-last-query))
(make-local-variable 'global-mode-string)
(add-to-list 'global-mode-string
`(:eval
(concat
(propertize
(mu4e-quote-for-modeline ,mu4e~headers-mode-line-label)
'face 'mu4e-modeline-face)
" "
(if (and mu4e-display-update-status-in-modeline
(buffer-live-p mu4e--update-buffer)
(process-live-p (get-buffer-process
mu4e--update-buffer)))
(propertize " (updating)" 'face 'mu4e-modeline-face)
""))))))
;;; Search-based marking ;;; Search-based marking

View File

@ -65,11 +65,6 @@ You can customize the exact fancy characters used with
:type 'boolean :type 'boolean
:group 'mu4e) :group 'mu4e)
(defcustom mu4e-display-update-status-in-modeline nil
"Non-nil value will display the update status in the modeline."
:group 'mu4e
:type 'boolean)
;; maybe move the next ones... but they're convenient ;; maybe move the next ones... but they're convenient
;; here because they're needed in multiple buffers. ;; here because they're needed in multiple buffers.
@ -234,101 +229,6 @@ Function returns the cdr of the list element."
(cdr chosen) (cdr chosen)
(mu4e-warn "Unknown shortcut '%c'" response)))) (mu4e-warn "Unknown shortcut '%c'" response))))
;;; Server properties
(defvar mu4e--server-props nil
"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."
mu4e--server-props)
(defun mu4e-root-maildir()
"Get the root maildir."
(or (and mu4e--server-props
(plist-get mu4e--server-props :root-maildir))
(mu4e-error "Root maildir unknown; did you start mu4e?")))
(defun mu4e-database-path()
"Get the root maildir."
(or (and mu4e--server-props
(plist-get mu4e--server-props :database-path))
(mu4e-error "Root maildir unknown; did you start mu4e?")))
(defun mu4e-server-version()
"Get the root maildir."
(or (and mu4e--server-props
(plist-get mu4e--server-props :version))
(mu4e-error "Version unknown; did you start mu4e?")))
(defun mu4e-last-query-results ()
"Get the results (counts) of the last cached queries.
The cached queries are the bookmark / maildir queries that are
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>
: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.
See `mu4e-last-query-results' for the format."
(seq-find
(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)))
;;; Logging / debugging ;;; Logging / debugging

View File

@ -1,6 +1,6 @@
;;; mu4e-main.el -- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;;; 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 <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -24,9 +24,9 @@
;;; Code: ;;; Code:
(require 'smtpmail) ;; the queueing stuff (silence elint) (require 'smtpmail)
(require 'mu4e-helpers) ;; utility functions (require 'mu4e-helpers)
(require 'mu4e-context) ;; the context (require 'mu4e-context)
(require 'mu4e-bookmarks) (require 'mu4e-bookmarks)
(require 'mu4e-folders) (require 'mu4e-folders)
(require 'mu4e-update) (require 'mu4e-update)
@ -58,18 +58,6 @@ the personal addresses."
:type 'boolean :type 'boolean
:group 'mu4e-main) :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 ;;; Mode
(define-derived-mode mu4e-org-mode org-mode "mu4e:org" (define-derived-mode mu4e-org-mode org-mode "mu4e:org"
@ -99,11 +87,11 @@ swiching to the main-view (using the `mu4e' command."
(interactive) (interactive)
(mu4e-info (concat mu4e-doc-dir "/NEWS.org"))) (mu4e-info (concat mu4e-doc-dir "/NEWS.org")))
(defun mu4e--main-reset-baseline-query-results () (defun mu4e--main-reset-baseline()
"Main-view version of `mu4e-reset-baseline-query-results'. "Main-view version of `mu4e-reset-query-results'.
This version handles updating the current screen as well." This version handles updating the current screen as well."
(interactive) (interactive)
(mu4e-reset-baseline-query-results) (mu4e--reset-baseline)
(revert-buffer)) (revert-buffer))
(defvar mu4e-main-mode-map (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 "f" #'smtpmail-send-queued-mail)
;; ;;
(define-key map "U" #'mu4e-update-mail-and-index) (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) (define-key map (kbd "C-S-u") #'mu4e-update-mail-and-index)
;; for terminal users ;; for terminal users
(define-key map (kbd "C-c C-u") #'mu4e-update-mail-and-index) (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-context-minor-mode)
(mu4e-search-minor-mode) (mu4e-search-minor-mode)
(mu4e-update-minor-mode) (mu4e-update-minor-mode)
(set (make-local-variable 'revert-buffer-function) #'mu4e--main-view-real) (setq-local revert-buffer-function
(add-hook 'mu4e-index-updated-hook #'mu4e--main-update-after-index)) (lambda (_ignore-auto _noconfirm)
(mu4e--main-view 'refresh))))
(defun mu4e--main-action-str (str &optional func-or-shortcut) (defun mu4e--main-action-str (str &optional func-or-shortcut)
"Highlight the first occurrence of [.] in STR. "Highlight the first occurrence of [.] in STR.
If FUNC-OR-SHORTCUT is non-nil and if it is a function, call it 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 when STR is clicked (using RET or mouse-2); if FUNC-OR-SHORTCUT is
a string, execute the corresponding keyboard action when it is a string, execute the corresponding keyboard action when it is
clicked." clicked. If HIGHLIGHT is non-nil, hightlight the name."
(let ((newstr (let ((newstr
(replace-regexp-in-string (replace-regexp-in-string
"\\[\\(..?\\)\\]" "\\[\\(..?\\)\\]"
@ -175,8 +164,7 @@ clicked."
(if (stringp func-or-shortcut) (if (stringp func-or-shortcut)
(length newstr) (length newstr)
(- (length newstr) 1)) (- (length newstr) 1))
'mouse-face 'highlight newstr) 'mouse-face 'highlight newstr) newstr))
newstr))
(defun mu4e--longest-of-maildirs-and-bookmarks () (defun mu4e--longest-of-maildirs-and-bookmarks ()
"Return the length of longest name of bookmarks and maildirs." "Return the length of longest name of bookmarks and maildirs."
@ -194,7 +182,11 @@ clicked."
(used for alignment)." (used for alignment)."
(concat (concat
(mu4e--main-action-str (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. ;; append all/unread numbers, if available.
(if qcounts (if qcounts
(let* ((unread (plist-get qcounts :unread)) (let* ((unread (plist-get qcounts :unread))
@ -203,32 +195,30 @@ clicked."
(baseline-unread (baseline-unread
(or (when baseline (plist-get baseline :unread)) unread)) (or (when baseline (plist-get baseline :unread)) unread))
(delta (- unread baseline-unread))) (delta (- unread baseline-unread)))
(format"%s (%s%s/%s)" (format "%s (%s%s/%s)"
(make-string (- max-length (string-width name)) ? ) (make-string (- max-length (string-width name)) ? )
(propertize (propertize
(number-to-string unread) 'face 'mu4e-header-key-face (number-to-string unread) 'face 'mu4e-header-key-face
'help-echo "Number of unread messages") 'help-echo "Number of unread messages")
(if (not mu4e-main-hide-baseline-delta) (if (> delta 0)
(propertize (format "/%+d" delta) 'face (propertize (format "(%+d)" delta) 'face
(if (> delta 0) 'mu4e-unread-face 'default) 'mu4e-unread-face) "")
'help-echo "Unread messsages baseline-delta")
"")
(propertize (number-to-string count) (propertize (number-to-string count)
'help-echo "Total number of messages"))) 'help-echo "Total number of messages")))
"") "\n")) "") "\n"))
(defun mu4e--main-items (shortcut items max-length) (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 - SHORTCUT is a single character which is the first
character of the keyboard shortcut 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 - MAX-LENGTH is the maximum length for an item name
(used for alignment)." (used for alignment)."
(cl-loop for item in items (cl-loop for item in items
for fullkey = (format "%c%c" shortcut (plist-get item :key)) for fullkey = (format "%c%c" shortcut (plist-get item :key))
for name = (plist-get item :name) for name = (plist-get item :name)
for query = (funcall (or mu4e-query-rewrite-function #'identity) 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 qcounts = (mu4e-last-query-result query)
for unread = (and qcounts (plist-get (car qcounts) :unread)) for unread = (and qcounts (plist-get (car qcounts) :unread))
when (not (plist-get item :hide)) when (not (plist-get item :hide))
@ -246,22 +236,17 @@ character of the keyboard shortcut
(propertize (concat " " unit) 'face 'mu4e-header-title-face) (propertize (concat " " unit) 'face 'mu4e-header-title-face)
"") "\n")) "") "\n"))
;; NEW This is the old `mu4e--main-view' function but without (defun mu4e--baseline-time-string ()
;; buffer switching at the end. "Calculate the baseline time string."
(defun mu4e--main-view-real (_ignore-auto _noconfirm) (let* ((baseline-t mu4e--baseline-tstamp)
"The revert buffer function for `mu4e-main-mode'." (updated-t (plist-get mu4e-index-update-status :tstamp))
(mu4e--main-view-real-1 'refresh)) (delta-t (and baseline-t updated-t
(float-time (time-subtract updated-t baseline-t)))))
(declare-function mu4e--start "mu4e") (if (and delta-t (> delta-t 0))
(format-seconds "%Y %D %H %M %z%S ago" delta-t)
(defun mu4e--main-view-real-1 (&optional refresh) (if baseline-t
"Create `mu4e-main-buffer-name' and set it up. (current-time-string baseline-t)
When REFRESH is non nil refresh infos from server." "Never"))))
(let ((inhibit-read-only t))
;; Maybe refresh infos from server.
(if refresh
(mu4e--start 'mu4e--main-redraw-buffer)
(mu4e--main-redraw-buffer))))
(defun mu4e--main-redraw-buffer () (defun mu4e--main-redraw-buffer ()
"Redraw the main 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 "mu4e" 'face 'mu4e-header-key-face)
(propertize " - mu for emacs version " 'face 'mu4e-title-face) (propertize " - mu for emacs version " 'face 'mu4e-title-face)
(propertize mu4e-mu-version 'face 'mu4e-header-key-face) (propertize mu4e-mu-version 'face 'mu4e-header-key-face)
"\n\n" "\n\n"
(propertize " Basics\n\n" 'face 'mu4e-title-face) (propertize " Basics\n\n" 'face 'mu4e-title-face)
(mu4e--main-action-str (mu4e--main-action-str
"\t* [j]ump to some maildir\n" #'mu4e~headers-jump-to-maildir) "\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) (propertize " Misc\n\n" 'face 'mu4e-title-face)
(mu4e--main-action-str "\t* [;]Switch context\n" (mu4e--main-action-str "\t* [;]Switch context\n"
(lambda()(interactive) (lambda()(interactive)
(mu4e-context-switch)(revert-buffer))) (mu4e-context-switch)(revert-buffer)))
(mu4e--main-action-str "\t* [U]pdate email & database\n" (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-action-str "\t* [R]eset baseline\n" #'mu4e--reset-baseline)
#'mu4e--main-reset-baseline-query-results)
;; show the queue functions if `smtpmail-queue-dir' is defined ;; show the queue functions if `smtpmail-queue-dir' is defined
(if (file-directory-p smtpmail-queue-dir) (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" (mu4e--key-val "last updated"
(current-time-string (current-time-string
(plist-get mu4e-index-update-status :tstamp))) (plist-get mu4e-index-update-status :tstamp)))
(if (and (not mu4e-main-hide-baseline-delta) (if mu4e--baseline-tstamp
mu4e--baseline-query-results-tstamp) (mu4e--key-val "baseline" (mu4e--baseline-time-string))
(mu4e--key-val "baseline"
(current-time-string mu4e--baseline-query-results-tstamp))
"") "")
(mu4e--key-val "database-path" (mu4e-database-path)) (mu4e--key-val "database-path" (mu4e-database-path))
(mu4e--key-val "maildir" (mu4e-root-maildir)) (mu4e--key-val "maildir" (mu4e-root-maildir))
(mu4e--key-val "in store" (mu4e--key-val "in store"
(format "%d" (plist-get mu4e--server-props :doccount)) (format "%d" (plist-get mu4e--server-props :doccount))
"messages") "messages")
(if mu4e-main-hide-personal-addresses "" (if mu4e-main-hide-personal-addresses ""
(mu4e--key-val "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 "" (if mu4e-main-hide-personal-addresses ""
(unless (mu4e-personal-address-p user-mail-address) (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))) (count-lines (point-min) (point-max)))
(error 0))) (error 0)))
(declare-function mu4e--start "mu4e")
(defun mu4e--main-view (&optional refresh) (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. When REFRESH is non nil refresh infos from server.
If `mu4e-split-view' equals \='single-window, show a mu4e menu If `mu4e-split-view' equals \='single-window, show a mu4e menu
instead." instead."
(mu4e--reset-baseline)
(if (eq mu4e-split-view 'single-window) (if (eq mu4e-split-view 'single-window)
(mu4e--main-menu) (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 ;; `mu4e--main-view' is called from `mu4e--start', so don't call it
;; a second time here i.e. do not refresh unless specified ;; a second time here i.e. do not refresh unless specified
;; explicitly with REFRESH arg. ;; explicitly with REFRESH arg.
(with-current-buffer buf (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) (mu4e-display-buffer buf t)
(goto-char (point-min))))) (goto-char (point-min)))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactive functions ;; Interactive functions
;; NEW
;; Toggle mail sending mode without switching ;; Toggle mail sending mode without switching
(defun mu4e--main-toggle-mail-sending-mode () (defun mu4e--main-toggle-mail-sending-mode ()
"Toggle sending mail mode, either queued or direct." "Toggle sending mail mode, either queued or direct."
@ -409,7 +396,7 @@ instead."
'(("jump" . mu4e~headers-jump-to-maildir) '(("jump" . mu4e~headers-jump-to-maildir)
("search" . mu4e-search) ("search" . mu4e-search)
("Compose" . mu4e-compose-new) ("Compose" . mu4e-compose-new)
("bookmarks" . mu4e-headers-search-bookmark) ("bookmarks" . mu4e-search-bookmark)
(";Switch context" . mu4e-context-switch) (";Switch context" . mu4e-context-switch)
("Update" . mu4e-update-mail-and-index) ("Update" . mu4e-update-mail-and-index)
("News" . mu4e-news) ("News" . mu4e-news)
@ -420,11 +407,5 @@ instead."
(sit-for 1) (sit-for 1)
(mu4e--main-menu)))) (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) (provide 'mu4e-main)
;;; mu4e-main.el ends here ;;; mu4e-main.el ends here

79
mu4e/mu4e-modeline.el Normal file
View File

@ -0,0 +1,79 @@
;;; mu4e-modeline.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:
;; This file contains functionality for putting mu4e-related information
;; in the emacs modeline, both buffer-specific and globally.
;;; Code:
(require 'cl-lib)
(defvar-local mu4e--modeline-buffer-items nil
"List of buffer-local items for the mu4e modeline.
Each element is function that evaluates to a string.")
(defvar mu4e--modeline-global-items nil
"List of items for the global modeline.
Each element is function that evaluates to a string.")
(defun mu4e--modeline-register (func &optional global)
"Register a function for calculating some mu4e modeline part.
If GLOBAL is non-nil, add to the global-modeline; otherwise use
the buffer-local one."
(add-to-list
(if global 'mu4e--modeline-global-items 'mu4e--modeline-buffer-items)
func))
(defvar mu4e--modeline-item nil
"Mu4e item for the global-mode-line.")
(defun mu4e--modeline-string ()
"Calculate the current mu4e modeline string."
(mapconcat
(lambda (item)
(if (functionp item)
(or (funcall item) "")
""))
(append mu4e--modeline-buffer-items mu4e--modeline-global-items) " "))
(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
;; buffer-specific parts (mu4e--modeline-buffer-items) and global items
;; (mu4e--modeline-global-items).
:global t
:group 'mu4e
:lighter nil
(if mu4e-modeline-mode
(progn
(setq mu4e--modeline-item '(:eval (mu4e--modeline-string)))
(add-to-list 'global-mode-string mu4e--modeline-item))
(progn
(setq global-mode-string
(seq-remove (lambda (item) (equal item mu4e--modeline-item))
global-mode-string))))
(force-mode-line-update))
(provide 'mu4e-modeline)
;; mu4e-modeline.el ends here

View File

@ -148,6 +148,8 @@
(define-obsolete-function-alias 'mu4e-read-query (define-obsolete-function-alias 'mu4e-read-query
'mu4e-search-read-query "1.7.0") 'mu4e-search-read-query "1.7.0")
(make-obsolete-variable 'mu4e-display-update-status-in-modeline
"No longer used" "1.9.11")
;; mu4e-headers ;; mu4e-headers
(make-obsolete-variable 'mu4e-headers-field-properties-function (make-obsolete-variable 'mu4e-headers-field-properties-function

View File

@ -165,8 +165,7 @@ but also manually invoked searches."
;;; History ;;; History
(defvar mu4e--search-query-past nil (defvar mu4e--search-query-past nil
"Stack of queries before the present one.") "Stack of queries before the present one.")
(defvar mu4e--search-query-future nil (defvar mu4e--search-query-future nil "Stack of queries after the present one.")
"Stack of queries after the present one.")
(defvar mu4e--search-query-stack-size 20 (defvar mu4e--search-query-stack-size 20
"Maximum size for the query stacks.") "Maximum size for the query stacks.")
(defvar mu4e--search-last-query nil (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 If EDIT is non-nil, let the user edit the bookmark before starting
the search." the search."
(interactive) (interactive)
(let ((expr (let* ((expr
(or 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) (run-hook-with-args 'mu4e-search-bookmark-hook expr)
(mu4e-search expr (when edit "Edit bookmark: ") edit))) (mu4e-search expr (when edit "Edit bookmark: ") edit)))

View File

@ -132,12 +132,55 @@ from the server process.")
(defvar mu4e-pong-func nil (defvar mu4e-pong-func nil
"Function called for each (:pong type ....) sexp received.") "Function called for each (:pong type ....) sexp received.")
(defvar mu4e-queries-func nil
"Function called for each (:queries type ....) sexp received.")
(defvar mu4e-contacts-func nil (defvar mu4e-contacts-func nil
"A function called for each (:contacts (<list-of-contacts>)) "A function called for each (:contacts (<list-of-contacts>))
sexp received from the server process.") sexp received from the server process.")
;;; Internal vars ;;; Dealing with Server properties
(defvar mu4e--server-props nil
"Metadata we receive from the mu4e server.")
(defun mu4e-server-properties ()
"Get the server metadata plist."
mu4e--server-props)
(defun mu4e-root-maildir()
"Get the root maildir."
(or (and mu4e--server-props
(plist-get mu4e--server-props :root-maildir))
(mu4e-error "Root maildir unknown; did you start mu4e?")))
(defun mu4e-database-path()
"Get the root maildir."
(or (and mu4e--server-props
(plist-get mu4e--server-props :database-path))
(mu4e-error "Root maildir unknown; did you start mu4e?")))
(defun mu4e-server-version()
"Get the root maildir."
(or (and mu4e--server-props
(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.")
(defun mu4e-server-query-results ()
"Get the latest server queries list."
mu4e--server-query-results)
;;; Handling raw server data
(defvar mu4e--server-buf nil (defvar mu4e--server-buf nil
"Buffer (string) for data received from the backend.") "Buffer (string) for data received from the backend.")
@ -164,8 +207,7 @@ Match 1 will be the length (in hex).")
Checks whether the server process is live." Checks whether the server process is live."
(and mu4e--server-process (and mu4e--server-process
(memq (process-status mu4e--server-process) (memq (process-status mu4e--server-process)
'(run open listen connect stop)) '(run open listen connect stop)) t))
t))
(defsubst mu4e--server-eat-sexp-from-buf () (defsubst mu4e--server-eat-sexp-from-buf ()
"'Eat' the next s-expression from `mu4e--server-buf'. "'Eat' the next s-expression from `mu4e--server-buf'.
@ -286,9 +328,14 @@ The server output is as follows:
;; received a pong message ;; received a pong message
((plist-get sexp :pong) ((plist-get sexp :pong)
(mu4e--update-server-props (plist-get sexp :props)) (setq mu4e--server-props (plist-get sexp :props))
(funcall mu4e-pong-func sexp)) (funcall mu4e-pong-func sexp))
;; receive queries info
((plist-get sexp :queries)
(setq mu4e--server-query-results (plist-get sexp :queries))
(funcall mu4e-queries-func sexp))
;; received a contacts message ;; received a contacts message
;; note: we use 'member', to match (:contacts nil) ;; note: we use 'member', to match (:contacts nil)
((plist-member sexp :contacts) ((plist-member sexp :contacts)
@ -562,11 +609,15 @@ Returns either (:update ... ) or (:error ) sexp, which are handled my
:rename ,(and maildir mu4e-change-filenames-when-moving t) :rename ,(and maildir mu4e-change-filenames-when-moving t)
:no-view ,(and no-view t)))) :no-view ,(and no-view t))))
(defun mu4e--server-ping (&optional queries) (defun mu4e--server-ping ()
"Sends a ping to the mu server, expecting a (:pong ...) in response. "Sends a ping to the mu server, expecting a (:pong ...) in response."
(mu4e--server-call-mu `(ping)))
(defun mu4e--server-queries (queries)
"Sends queries to the mu server, expecting a (:queries ...) in response.
QUERIES is a list of queries for the number of results with QUERIES is a list of queries for the number of results with
read/unread status are returned in the pong-response." read/unread status are returned in the pong-response."
(mu4e--server-call-mu `(ping :queries ,queries))) (mu4e--server-call-mu `(queries :queries ,queries)))
(defun mu4e--server-remove (docid) (defun mu4e--server-remove (docid)
"Remove message with DOCID. "Remove message with DOCID.

View File

@ -311,7 +311,6 @@ run in the background; otherwise, pop up a window."
:lighter "" :lighter ""
:keymap :keymap
(let ((map (make-sparse-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) (define-key map (kbd "C-S-u") #'mu4e-update-mail-and-index)
;; for terminal users ;; for terminal users
(define-key map (kbd "C-c C-u") #'mu4e-update-mail-and-index) (define-key map (kbd "C-c C-u") #'mu4e-update-mail-and-index)

View File

@ -1,6 +1,6 @@
;;; mu4e.el --- part of mu4e, the mu mail user agent -*- lexical-binding: t -*- ;;; mu4e.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 <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -48,6 +48,11 @@
:type 'boolean :type 'boolean
:group 'mu4e) :group 'mu4e)
(defcustom mu4e-modeline-support t
"Support for shoiwing information in the modeline."
:type 'boolean
:group 'mu4e)
(defcustom mu4e-org-support t (defcustom mu4e-org-support t
"Support Org-mode links." "Support Org-mode links."
:type 'boolean :type 'boolean
@ -68,6 +73,7 @@
(with-eval-after-load 'desktop (with-eval-after-load 'desktop
(eval '(add-to-list 'desktop-modes-not-to-save 'mu4e-compose-mode))) (eval '(add-to-list 'desktop-modes-not-to-save 'mu4e-compose-mode)))
;;;###autoload ;;;###autoload
(defun mu4e (&optional background) (defun mu4e (&optional background)
"If mu4e is not running yet, start it. "If mu4e is not running yet, start it.
@ -76,12 +82,11 @@ is non-nil."
(interactive "P") (interactive "P")
;; start mu4e, then show the main view ;; start mu4e, then show the main view
(mu4e--init-handlers) (mu4e--init-handlers)
;; i.e., only auto update baseline when user explicitly requested ;; i.e., only auto update baseline when user explicitly requested
(when (and mu4e-main-auto-reset-baseline (when (or (not mu4e--baseline-tstamp)
(not background) (called-interactively-p 'interactive)) (and (not background) (called-interactively-p 'interactive)))
(mu4e-reset-baseline-query-results)) (mu4e--reset-baseline))
(mu4e--modeline-update)
(mu4e--start (unless background 'mu4e--main-view))) (mu4e--start (unless background 'mu4e--main-view)))
(defun mu4e-quit() (defun mu4e-quit()
@ -133,6 +138,34 @@ Invoke FUNC if non-nil."
(lambda () (mu4e-update-mail-and-index (lambda () (mu4e-update-mail-and-index
mu4e-index-update-in-background))))))) 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 'force-update)
(unless mu4e--baseline
(mu4e--reset-baseline))
(mu4e--modeline-update)
;; 2. update the main view, if any
(when (buffer-live-p mu4e-main-buffer-name)
(with-current-buffer mu4e-main-buffer-name
(revert-buffer))))
(defun mu4e--start (&optional func) (defun mu4e--start (&optional func)
"Start mu4e. "Start mu4e.
If `mu4e-contexts' have been defined, but we don't have a context If `mu4e-contexts' have been defined, but we don't have a context
@ -144,18 +177,10 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(unless (mu4e-context-current) (unless (mu4e-context-current)
(mu4e--context-autoswitch nil mu4e-context-policy)) (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--server-ping (mu4e--modeline-register #'mu4e--bookmarks-modeline-item 'global)
(mapcar ;; send it a list of queries we'd like to see read/unread info for (mu4e-modeline-mode (if mu4e-modeline-support 1 -1))
(lambda (bm) (mu4e--server-bookmarks-queries)
(funcall (or mu4e-query-rewrite-function #'identity) (mu4e--server-ping)
(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)))))
;; maybe request the list of contacts, automatically refreshed after ;; maybe request the list of contacts, automatically refreshed after
;; reindexing ;; reindexing
(unless mu4e--contacts-set (mu4e--request-contacts-maybe))) (unless mu4e--contacts-set (mu4e--request-contacts-maybe)))
@ -166,6 +191,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
(cancel-timer mu4e--update-timer) (cancel-timer mu4e--update-timer)
(setq mu4e--update-timer nil)) (setq mu4e--update-timer nil))
(mu4e-clear-caches) (mu4e-clear-caches)
(mu4e-modeline-mode -1)
(mu4e--server-kill) (mu4e--server-kill)
;; kill all mu4e buffers ;; kill all mu4e buffers
(mapc (mapc
@ -185,7 +212,7 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
;;; Handlers ;;; Handlers
(defun mu4e--default-handler (&rest args) (defun mu4e--default-handler (&rest args)
"Dummy handler function with arbitrary 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) (defun mu4e--error-handler (errcode errmsg)
"Handler function for showing an error with ERRCODE and ERRMSG." "Handler function for showing an error with ERRCODE and ERRMSG."
@ -222,6 +249,8 @@ Otherwise, check requirements, then start mu4e. When successful, invoke
"%s completed; checked %d, updated %d, cleaned-up %d" "%s completed; checked %d, updated %d, cleaned-up %d"
(if mu4e-index-lazy-check "Lazy indexing" "Indexing") (if mu4e-index-lazy-check "Lazy indexing" "Indexing")
checked updated cleaned-up) checked updated cleaned-up)
;; index done; grab updated queries
(mu4e--server-bookmarks-queries)
(run-hooks 'mu4e-index-updated-hook) (run-hooks 'mu4e-index-updated-hook)
;; backward compatibility... ;; backward compatibility...
(unless (zerop (+ updated cleaned-up)) (unless (zerop (+ updated cleaned-up))
@ -248,11 +277,12 @@ chance."
(mu4e-setq-if-nil mu4e-found-func #'mu4e~headers-found-handler) (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-erase-func #'mu4e~headers-clear)
(mu4e-setq-if-nil mu4e-sent-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-compose-func #'mu4e~compose-handler)
(mu4e-setq-if-nil mu4e-contacts-func #'mu4e--update-contacts) (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-info-func #'mu4e--info-handler)
(mu4e-setq-if-nil mu4e-pong-func #'mu4e--default-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 () (defun mu4e-clear-caches ()
"Clear any cached resources." "Clear any cached resources."

View File

@ -12,7 +12,7 @@
@c %**end of header @c %**end of header
@copying @copying
Copyright @copyright{} 2012-2022 Dirk-Jan C. Binnema Copyright @copyright{} 2012-2023 Dirk-Jan C. Binnema
@quotation @quotation
Permission is granted to copy, distribute and/or modify this document Permission is granted to copy, distribute and/or modify this document
@ -80,7 +80,7 @@ basic configuration and explain its daily use. We also show you how you
can customize @t{mu4e} for your special needs. can customize @t{mu4e} for your special needs.
At the end of the manual, there are some example configurations, to get At the end of the manual, there are some example configurations, to get
you up to speed quickly: @ref{Example configs}. There's also a section you up to speed quickly: @ref{Example configurations}. There's also a section
with answers to frequently asked questions, @ref{FAQ}. with answers to frequently asked questions, @ref{FAQ}.
@menu @menu
@ -99,25 +99,24 @@ with answers to frequently asked questions, @ref{FAQ}.
Appendices Appendices
* Other tools:: mu4e and the rest of the world * Other tools:: mu4e and the rest of the world
* Example configs:: Some examples to set you up quickly * Example configurations:: Some examples to set you up quickly
* FAQ:: Common questions and answers * FAQ:: Common questions and answers
* Tips and Tricks:: Useful tips * Tips and Tricks:: Useful tips
* How it works:: Some notes about the implementation of @t{mu4e} * How it works:: Some notes about the implementation of @t{mu4e}
* Debugging:: How to debug problems in @t{mu4e} * Debugging:: How to debug problems in @t{mu4e}
* GNU Free Documentation License:: The license of this manual * GNU Free Documentation License:: The license of this manual
@c Indices Indices
@c * Command Index:: An item for each standard command name. @c * Command Index:: An item for each standard command name.
@c * Variable Index:: An item for each variable documented in this manual. @c * Variable Index:: An item for each variable documented in this manual.
@c * Concept Index:: An item for concepts and other general subjects. * Concept Index:: Index of @t{mu4e} concepts and other general subjects.
@end menu @end menu
@node Introduction @node Introduction
@chapter Introduction @chapter Introduction
Let's get started! Let's get started!q
@menu @menu
* Why another e-mail client::Aren't there enough already * Why another e-mail client::Aren't there enough already
* Other mail clients::Where @t{mu4e} takes its inspiration from * Other mail clients::Where @t{mu4e} takes its inspiration from
@ -316,7 +315,7 @@ Note: if you are familiar with @t{meson}, you can of course use its
commands directly; the @t{make} commands are just a thin wrapper around commands directly; the @t{make} commands are just a thin wrapper around
that. that.
@subsection Installing @subsection Installation
After this, @t{mu} and @t{mu4e} should be installed @footnote{there's a After this, @t{mu} and @t{mu4e} should be installed @footnote{there's a
hard dependency between versions of @t{mu4e} and @t{mu} --- you cannot hard dependency between versions of @t{mu4e} and @t{mu} --- you cannot
@ -337,7 +336,7 @@ configuration before trying again:
There is some support for using the Emacs customization system in There is some support for using the Emacs customization system in
@t{mu4e}, but for now, we recommend setting the values manually. Please @t{mu4e}, but for now, we recommend setting the values manually. Please
refer to @ref{Example configs} for a couple of examples of this; here we refer to @ref{Example configurations} for a couple of examples of this; here we
go through things step-by-step. go through things step-by-step.
@node Getting mail @node Getting mail
@ -394,7 +393,7 @@ You can add some e-mail addresses, so @t{mu} recognizes them as yours:
indexing messages. If you want to change them, you need to @t{init} indexing messages. If you want to change them, you need to @t{init}
once again. once again.
The addresses may also be basic POSIX regular expressions, wrapped in The addresses may also be basic PCRE regular expressions, wrapped in
slashes, for example: slashes, for example:
@example @example
@ -603,7 +602,7 @@ For sending mail using @abbr{SMTP}, @t{mu4e} uses @t{smtpmail}
send mail; please refer to its documentation for the details. send mail; please refer to its documentation for the details.
Here, we only provide some simple examples --- for more, see Here, we only provide some simple examples --- for more, see
@ref{Example configs}. @ref{Example configurations}.
A very minimal setup: A very minimal setup:
@ -716,18 +715,18 @@ The main view looks something like the following:
Bookmarks Bookmarks
* [bu] Unread messages (13085/+3/13085) * [bu] Unread messages (13085(+3)/13085)
* [bt] Today's messages * [bt] Today's messages
* [bw] Last 7 days (53/+3/128) * [bw] Last 7 days (53(+3)/128)
* [bp] Messages with images (75/-2/2441) * [bp] Messages with images (75/2441)
Maildirs Maildirs
* [ja] /archive (2101/0/18837) * [ja] /archive (2101/18837)
* [ji] /inbox (8/+2/10) * [ji] /inbox (8(+2)/10)
* [jb] /bulk (33/+0/35) * [jb] /bulk (33/35)
* [jB] /bulkarchive (179/+0/2090) * [jB] /bulkarchive (179/2090)
* [jm] /mu (694/+1/17687) * [jm] /mu (694(+1)/17687)
* [jn] /sauron * [jn] /sauron
* [js] /sent * [js] /sent
@ -780,26 +779,30 @@ the @ref{Editor view} to write a new message.
The next two items in the Main view are @emph{Bookmarks} and @emph{Maildirs}. The next two items in the Main view are @emph{Bookmarks} and @emph{Maildirs}.
Bookmarks are predefined queries with a descriptive name and a Bookmarks are predefined queries with a descriptive name and a shortcut --- in
shortcut --- in the example above, we see the default bookmarks. You the example above, we see the default bookmarks. You can view the list of
can view the list of messages matching a certain bookmark by pressing messages matching a certain bookmark by pressing @key{b} followed by the
@key{b} followed by the bookmark's shortcut. If you'd like to edit the bookmark's shortcut. If you want to edit the bookmarked query before invoking
bookmarked query first before invoking it, use @key{B}. it, use @key{B}.
Next to each bookmark there is the number of (unread/delta/all) messages that Next to each bookmark are some numbers that indicate the (unread(delta)/all)
match. matching messages for the given query, with the delta being the difference in
unread count since some ``baseline'', and only shown when this delta > 0.
The ``unread'' and ``all'' have their obvious meaning; the @emph{delta} is the Note that the ``delta'' has its limitations: if you, for instance, deleted 5
difference in unread messages since ``baseline'', which is the state at some messages and received 5 new one, the ``delta'' would be 0, although there were
point in the past. This delta is useful to quickly see what changed since the changes indeed. So it is mostly useful for tracking changes while you are
last time you looked. @emph{not} using @t{mu4e}.
By default, the baseline is automatically reset when switching to the main view This delta is useful to quickly see that there are new messages since the last
explicitly; otherwise the baseline can be reset with time you looked. Imagine switching to another buffer to work on something or
@code{mu4e-reset-baseline-query-results}, which is bound to @kbd{R} in the even (!) stepping away from your computer to return later: the baseline allows
main-view, and @kbd{C-c C-R} throughout @t{mu4e}. You can customize the behavior you to quickly see what changed.
using @var{mu4e-main-hide-baseline-delta} and
@var{mu4e-main-auto-reset-baseline}. By default, the baseline is reset automatically when switching to the main view
explicitly (but see @code{mu4e-main-auto-reset-baseline}. It can also be reset
explicitly using @code{mu4e-reset-baseline-query-results}, which is bound to
@kbd{R} in the main-view.
Bookmarks are stored in the variable @code{mu4e-bookmarks}; you can add Bookmarks are stored in the variable @code{mu4e-bookmarks}; you can add
your own and/or replace the default ones; @xref{Bookmarks}. For your own and/or replace the default ones; @xref{Bookmarks}. For
@ -812,14 +815,17 @@ instance:
:query "list:mu-discuss.googlegroups.com AND date:7d..now")) :query "list:mu-discuss.googlegroups.com AND date:7d..now"))
@end lisp @end lisp
There are optional keys @t{:hide} to hide the bookmark from the main There are optional keys @t{:hide} to hide the bookmark from the main menu, but
menu, but still have it available (using @key{b})) and still have it available (using @key{b})) and @t{:hide-unread} to avoid
@t{:hide-unread} to avoid generating the unread-number; that can be generating the unread-number; that can be useful if you have bookmarks for slow
useful if you have bookmarks for slow queries. Note that queries. Note that @t{:hide-unread} is implied when the query is not a string;
@t{:hide-unread} is implied when the query is not a string; this for this for the common case where the query function involves some user input,
the common case where the query function involves some user input,
which would be disruptive in this case. which would be disruptive in this case.
There is also the optional @code{:favorite} property, which at most one bookmark
should have; this bookmark is highlighted in the main view, and its
unread-status is shown in the modeline; @xref{Modeline}.
The Maildirs item is very similar to Bookmarks, when you consider maildirs here The Maildirs item is very similar to Bookmarks, when you consider maildirs here
as being special kind of bookmark that matches some Maildir. as being special kind of bookmark that matches some Maildir.
@ -1071,7 +1077,7 @@ Note that with threading enabled, the sorting is exclusively by date,
regardless of the column clicked. regardless of the column clicked.
If you want to change the defaults for these settings, you can use the variables If you want to change the defaults for these settings, you can use the variables
@var{mu4e-search-sort-field} and @var{mu4e-search-show-threads}, as well as @code{mu4e-search-sort-field} and @code{mu4e-search-show-threads}, as well as
@code{mu4e-search-change-sorting} to change the sorting of the current search @code{mu4e-search-change-sorting} to change the sorting of the current search
results. results.
@ -1740,7 +1746,7 @@ then still include the signature manually, using the function
@itemize @itemize
@item If you want use @t{mu4e} as Emacs' default program for sending mail, @item If you want use @t{mu4e} as Emacs' default program for sending mail,
see @ref{Emacs default}. see @ref{Default email client}.
@item Normally, @t{mu4e} @emph{buries} the message buffer after sending; if you want @item Normally, @t{mu4e} @emph{buries} the message buffer after sending; if you want
to kill the buffer instead, add something like the following to your to kill the buffer instead, add something like the following to your
configuration: configuration:
@ -2099,7 +2105,7 @@ query, so, effectively `widens' the search. Technically, narrowing the results
of query @t{x} with expression @t{y} implies doing a search @t{(x) AND (y)}. of query @t{x} with expression @t{y} implies doing a search @t{(x) AND (y)}.
Note that messages that were not in your original search results because of Note that messages that were not in your original search results because of
@var{mu4e-search-results-limit} may show up in the narrowed query. @code{mu4e-search-results-limit} may show up in the narrowed query.
@subsection Including related messages @subsection Including related messages
@anchor{Including related messages} @anchor{Including related messages}
@ -2108,7 +2114,7 @@ It can be useful to not only show the messages that directly match a certain
query, but also include messages that are related to these messages. That is, query, but also include messages that are related to these messages. That is,
messages that belong to the same discussion threads are included in the results, messages that belong to the same discussion threads are included in the results,
just like e.g. Gmail does it. You can enable this behavior by setting just like e.g. Gmail does it. You can enable this behavior by setting
@var{mu4e-search-include-related} to @code{t}, and you can toggle between @code{mu4e-search-include-related} to @code{t}, and you can toggle between
including/not-including with @key{W}. including/not-including with @key{W}.
Be careful though when e.g. deleting ranges of messages from a certain Be careful though when e.g. deleting ranges of messages from a certain
@ -2123,7 +2129,7 @@ copies of messages, there's usually little value in including more than one in
search results. A common reason for having multiple copies of messages is the search results. A common reason for having multiple copies of messages is the
combination of Gmail and @t{offlineimap}, since that is the way the labels / combination of Gmail and @t{offlineimap}, since that is the way the labels /
virtual folders in Gmail are represented. You can enable skipping duplicates by virtual folders in Gmail are represented. You can enable skipping duplicates by
setting @var{mu4e-search-skip-duplicates} to @code{t}, and you can toggle setting @code{mu4e-search-skip-duplicates} to @code{t}, and you can toggle
between the skipping/not skipping with @key{V}. between the skipping/not skipping with @key{V}.
Note, messages are considered duplicates when they have the same Note, messages are considered duplicates when they have the same
@ -2265,8 +2271,8 @@ Custom mark functions are to be appended to the list
@item The name of the marker --- a short string describing this marker. The @item The name of the marker --- a short string describing this marker. The
first character of this string determines its shortcut, so these should be first character of this string determines its shortcut, so these should be
unique. If necessary, simply prefix the name with a unique character. unique. If necessary, simply prefix the name with a unique character.
@item a predicate function, taking two arguments @var{msg} and @var{param}. @item a predicate function, taking two arguments @code{msg} and @code{param}.
@var{msg} is the message plist (see @ref{Message functions}) and @var{param} is @code{msg} is the message plist (see @ref{Message functions}) and @code{param} is
a parameter provided by the third of the marker elements (see the next a parameter provided by the third of the marker elements (see the next
item). The predicate function should return non-@t{nil} if the message item). The predicate function should return non-@t{nil} if the message
matches. matches.
@ -2575,8 +2581,8 @@ when starting; see the discussion in the previous section.
A couple of notes about this example: A couple of notes about this example:
@itemize @itemize
@item You can manually switch the context use @code{M-x mu4e-context-switch}, @item You can manually switch the context use @code{M-x mu4e-context-switch},
by default bound to @kbd{;} in headers, view and main mode. by default bound to @kbd{;} in headers, view and main mode. The current context
The current context appears in the mode-line. appears in the modeline by default; see @ref{Modeline} for details.
@item Normally, @code{M-x mu4e-context-switch} does not call the enter or @item Normally, @code{M-x mu4e-context-switch} does not call the enter or
leave functions if the 'new' context is the same as the old one. leave functions if the 'new' context is the same as the old one.
However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to However, with a prefix-argument (@kbd{C-u}), you can force @t{mu4e} to
@ -2713,11 +2719,10 @@ work-email. You can achieve this with something like:
@noindent @noindent
Good to remember: Good to remember:
@itemize @itemize
@item The @var{msg} parameter you receive in the function refers to the @item The @code{msg} parameter you receive in the function refers to the
@emph{original message}, that is, the message being replied to or @emph{original message}, that is, the message being replied to or forwarded.
forwarded. When re-editing a message, it refers to the message being When re-editing a message, it refers to the message being edited. When you
edited. When you compose a totally new message, the @var{msg} parameter is compose a totally new message, the @code{msg} parameter is @code{nil}.
@code{nil}.
@item When re-editing messages, the value of @code{mu4e-drafts-folder} is ignored. @item When re-editing messages, the value of @code{mu4e-drafts-folder} is ignored.
@end itemize @end itemize
@ -3052,32 +3057,84 @@ see @code{mu4e-toggle-logging}.
@code{user-error} and @code{error} functions. @code{user-error} and @code{error} functions.
@end itemize @end itemize
@node Other tools @node Integrating with Emacs
@appendix Other tools @chapter Integrating with Emacs
In this chapter, we discuss some ways in which @t{mu4e} can cooperate In this chapter, we discuss how you can integrate @t{mu4e} with Emacs in various
with other tools. ways. Here we focus on Emacs built-ins; for dealing with external tools,
@xref{Other tools}.
@menu @menu
* Emacs default::Making mu4e the default emacs e-mail program * Modeline::Showing mu4e's status in the modeline
* Emacs bookmarks::Using Emacs' bookmark system * Default email client::Making mu4e the default emacs e-mail program
* Using bookmarks::Using Emacs' bookmark system
* Org-mode links::Adding mu4e to your organized life * Org-mode links::Adding mu4e to your organized life
* Org-contacts::Hooking up with org-contacts
* BBDB::Hooking up with the Insidious Big Brother Database
* iCalendar::Enabling iCalendar invite processing * iCalendar::Enabling iCalendar invite processing
* Sauron::Getting new mail notifications with Sauron
* Speedbar::A special frame with your folders * Speedbar::A special frame with your folders
* Dired:: Attaching files using @t{dired} * Dired:: Attaching files using @t{dired}
* Hydra:: Custom shortcut menus
@end menu @end menu
@node Emacs default @node Modeline
@section Emacs default @section Modeline
@cindex modeline
Emacs allows you to select an e-mail program as the default One of the most visible ways in which @t{mu4e} integrates with Emacs is through
program it uses when you press @key{C-x m} (@code{compose-mail}), call the @emph{modeline} @xref{Mode Line,,,emacs}. The @t{mu4e} support for that is
@code{report-emacs-bug} and so on. If you want to use @t{mu4e} for this, handled through a minor-mode @code{mu4e-modeline-mode}, which is enabled by
you can do so by adding the following to your configuration: default when @t{mu4e} is running.
To completely turn off the modeline support, set @code{mu4e-support-modeline} to
@t{nil} before starting @t{mu4e}.
@t{mu4e} shares information on the modeline in two ways:
@itemize
@item buffer-specific: information about the search parameters and the current context
@item global: information about unread messages
@end itemize
The buffer-specific items should be fairly self-explanatory - they show,
respectively, your search parameters and the current context.
@cindex favorite bookmark
Since @t{mu4e} is a query-based email-client, there a lot of flexibilty in what
you want to consider ``interesting new mail'', that is the, the query we want to
monitor for changes.
The global modeline contains the results of some specific ``favorite'' bookmark
query from @code{mu4e-bookmarks}. By default, the @emph{first} one in chosen,
but you may want change that by using the @code{:favorite} property for a
particular query, e.g., as part of @var{mu4e-bookmarks}:
@example
;; Monitor the inbox folder in the modeline
(:query "maildir:/inbox" :name "Inbox" :key ?i :favorite t)
@end example
The results of this query (the last time it was updated) is shows as some
character or emoji (depending on @var{mu4e-use-fancy-chars}) and 2 or 3 numbers,
just like what we saw in @xref{Bookmarks and Maildirs}, e.g.,
@example
N:10(+5)/15
@end example
@cindex baseline query results
this means there are 10 unread messages, with 5 new messages since the baseline,
and in total 15 messages.
You can also customize the icon; see @var{mu4e-modeline-all-clear},
@var{mu4e-modeline-all-read}, @var{mu4e-modeline-all-clear} and
@var{mu4e-modeline-all-clear}
Due to the way queries work, the modeline is @emph{not} immediately updated when
you read messages; but going back to the main view (with @kbd{M-x mu4e} restores
things.
@node Default email client
@section Default email client
Emacs allows you to select an e-mail program as the default program it uses when
you press @key{C-x m} (@code{compose-mail}), call @code{report-emacs-bug} and so
on. If you want to use @t{mu4e} for this, you can do so by adding the following
to your configuration:
@lisp @lisp
(setq mail-user-agent 'mu4e-user-agent) (setq mail-user-agent 'mu4e-user-agent)
@ -3092,15 +3149,15 @@ mail, customize the variable @code{read-mail-command}.
(@pxref{Top,,Emacs,Sending Mail, Mail Methods}) (@pxref{Top,,Emacs,Sending Mail, Mail Methods})
@node Using bookmarks
@section Using bookmarks
@node Emacs bookmarks Note, emacs bookmarks are not to be confused with mu4e's bookmarks; the former
@section Emacs bookmarks are a generic linking system, while the latter are remember stored queries.
@t{mu4e} supports linking to the message-at-point through the normal
Emacs built-in bookmark system. The links are based on the message's
message-id, and thus the bookmarks stay valid even if you move the
message around.
@t{mu4e} supports linking to the message-at-point through the normal Emacs
built-in bookmark system. The links are based on the message's message-id, and
thus the bookmarks stay valid even if you move the message around.
@node Org-mode links @node Org-mode links
@section Org-mode links @section Org-mode links
@ -3164,71 +3221,6 @@ key-bindings for that in headers and view mode:
(define-key mu4e-view-mode-map (kbd "C-c c") 'mu4e-org-store-and-capture) (define-key mu4e-view-mode-map (kbd "C-c c") 'mu4e-org-store-and-capture)
@end lisp @end lisp
@node Org-contacts
@section Org-contacts
Note, @t{mu4e} supports built-in address autocompletion; @ref{Address
autocompletion}, and that is the recommended way to do this. However, it
is also possible to manage your addresses with @t{org-mode}, using
@t{org-contacts}@footnote{@url{https://julien.danjou.info/projects/emacs-packages#org-contacts}}.
@t{mu4e-actions} defines a useful action (@ref{Actions}) for adding a
contact based on the @t{From:}-address in the message at point. To
enable this, add to your configuration something like:
@lisp
(setq mu4e-org-contacts-file <full-path-to-your-org-contacts-file>)
(add-to-list 'mu4e-headers-actions
'("org-contact-add" . mu4e-action-add-org-contact) t)
(add-to-list 'mu4e-view-actions
'("org-contact-add" . mu4e-action-add-org-contact) t)
@end lisp
@noindent
After this, you should be able to add contacts using @key{a o} in the
headers view and the message view, using the @t{org-capture} mechanism.
Note, the shortcut character @key{o} is due to the first character of
@t{org-contact-add}.
@node BBDB
@section BBDB
Note, @t{mu4e} supports built-in address autocompletion; @ref{Address
autocompletion}, and that is the recommended way to do this. However, it
is also possible to manage your addresses with the current (2015-06-23)
development release of @t{BBDB}, or releases of @t{BBDB} after
3.1.2.@footnote{@url{https://savannah.nongnu.org/projects/bbdb/}}.
To enable BBDB, add to your @file{~/.emacs} (or its moral equivalent,
such as @file{~/.emacs.d/init.el}) the following @emph{after} the
@code{(require 'mu4e)} line:
@lisp
;; Load BBDB (Method 1)
(require 'bbdb-loaddefs)
;; OR (Method 2)
;; (require 'bbdb-loaddefs "/path/to/bbdb/lisp/bbdb-loaddefs.el")
;; OR (Method 3)
;; (autoload 'bbdb-insinuate-mu4e "bbdb-mu4e")
;; (bbdb-initialize 'message 'mu4e)
(setq bbdb-mail-user-agent 'mu4e-user-agent)
(setq mu4e-view-rendered-hook 'bbdb-mua-auto-update)
(setq mu4e-compose-complete-addresses nil)
(setq bbdb-mua-pop-up t)
(setq bbdb-mua-pop-up-window-size 5)
(setq mu4e-view-show-addresses t)
@end lisp
@noindent
After this, you should be able to:
@itemize
@item In mu4e-view mode, add the sender of the email to BBDB with @key{C-u :}
@item Tab-complete addresses from BBDB when composing emails
@item View the BBDB contact while viewing a message
@end itemize
@node iCalendar @node iCalendar
@section iCalendar @section iCalendar
@ -3320,6 +3312,120 @@ functions use that to set the @code{datetree} location:
If you do this, you'll want to omit the @code{:timeprompt t} setting If you do this, you'll want to omit the @code{:timeprompt t} setting
from your capture template. from your capture template.
@node Speedbar
@section Speedbar
@cindex speedbar
@code{speedbar} is an Emacs-extension that shows navigational
information for an Emacs buffer in a separate frame. Using
@code{mu4e-speedbar}, @t{mu4e} lists your bookmarks and maildir
folders and allows for one-click access to them.
To enable this, add @t{(require 'mu4e-speedbar)} to your configuration;
then, all you need to do to activate it is @kbd{M-x speedbar}. Then,
when then switching to the @ref{Main view}, the speedbar-frame is
updated with your bookmarks and maildirs.
For speed reasons, the list of maildirs is determined when @t{mu4e}
starts; if the list of maildirs changes while @t{mu4e} is running, you
need to restart @t{mu4e} to have those changes reflected in the speedbar
and in other places that use this list, such as auto-completion when
jumping to a maildir.
@node Dired
@section Dired
It is possible to attach files to @t{mu4e} messages using @t{dired}
(@ref{Dired,,emacs}), using the following steps (based on a post on
the @t{mu-discuss} mailing list by @emph{Stephen Eglen}).
@lisp
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)
@end lisp
Then, mark the file(s) in @t{dired} you would like to attach and press
@t{C-c RET C-a}, and you'll be asked whether to attach them to an
existing message, or create a new one.
@node Other tools
@appendix Other tools
In this chapter, we discuss some ways in which @t{mu4e} can cooperate
with other tools.
@menu
* Org-contacts::Hooking up with org-contacts
* BBDB::Hooking up with the Insidious Big Brother Database
* Sauron::Getting new mail notifications with Sauron
* Hydra:: Custom shortcut menus
@end menu
@node Org-contacts
@section Org-contacts
Note, @t{mu4e} supports built-in address autocompletion; @ref{Address
autocompletion}, and that is the recommended way to do this. However, it
is also possible to manage your addresses with @t{org-mode}, using
@t{org-contacts}@footnote{@url{https://julien.danjou.info/projects/emacs-packages#org-contacts}}.
@t{mu4e-actions} defines a useful action (@ref{Actions}) for adding a
contact based on the @t{From:}-address in the message at point. To
enable this, add to your configuration something like:
@lisp
(setq mu4e-org-contacts-file <full-path-to-your-org-contacts-file>)
(add-to-list 'mu4e-headers-actions
'("org-contact-add" . mu4e-action-add-org-contact) t)
(add-to-list 'mu4e-view-actions
'("org-contact-add" . mu4e-action-add-org-contact) t)
@end lisp
@noindent
After this, you should be able to add contacts using @key{a o} in the
headers view and the message view, using the @t{org-capture} mechanism.
Note, the shortcut character @key{o} is due to the first character of
@t{org-contact-add}.
@node BBDB
@section BBDB
Note, @t{mu4e} supports built-in address autocompletion; @ref{Address
autocompletion}, and that is the recommended way to do this. However, it
is also possible to manage your addresses with the current (2015-06-23)
development release of @t{BBDB}, or releases of @t{BBDB} after
3.1.2.@footnote{@url{https://savannah.nongnu.org/projects/bbdb/}}.
To enable BBDB, add to your @file{~/.emacs} (or its moral equivalent,
such as @file{~/.emacs.d/init.el}) the following @emph{after} the
@code{(require 'mu4e)} line:
@lisp
;; Load BBDB (Method 1)
(require 'bbdb-loaddefs)
;; OR (Method 2)
;; (require 'bbdb-loaddefs "/path/to/bbdb/lisp/bbdb-loaddefs.el")
;; OR (Method 3)
;; (autoload 'bbdb-insinuate-mu4e "bbdb-mu4e")
;; (bbdb-initialize 'message 'mu4e)
(setq bbdb-mail-user-agent 'mu4e-user-agent)
(setq mu4e-view-rendered-hook 'bbdb-mua-auto-update)
(setq mu4e-compose-complete-addresses nil)
(setq bbdb-mua-pop-up t)
(setq bbdb-mua-pop-up-window-size 5)
(setq mu4e-view-show-addresses t)
@end lisp
@noindent
After this, you should be able to:
@itemize
@item In mu4e-view mode, add the sender of the email to BBDB with @key{C-u :}
@item Tab-complete addresses from BBDB when composing emails
@item View the BBDB contact while viewing a message
@end itemize
@node Sauron @node Sauron
@section Sauron @section Sauron
@ -3377,40 +3483,6 @@ You might want to put:
in your setup, to allow the script to find the D-Bus session bus, even when in your setup, to allow the script to find the D-Bus session bus, even when
running outside its session. running outside its session.
@node Speedbar
@section Speedbar
@code{speedbar} is an Emacs-extension that shows navigational
information for an Emacs buffer in a separate frame. Using
@code{mu4e-speedbar}, @t{mu4e} lists your bookmarks and maildir
folders and allows for one-click access to them.
To enable this, add @t{(require 'mu4e-speedbar)} to your configuration;
then, all you need to do to activate it is @kbd{M-x speedbar}. Then,
when then switching to the @ref{Main view}, the speedbar-frame is
updated with your bookmarks and maildirs.
For speed reasons, the list of maildirs is determined when @t{mu4e}
starts; if the list of maildirs changes while @t{mu4e} is running, you
need to restart @t{mu4e} to have those changes reflected in the speedbar
and in other places that use this list, such as auto-completion when
jumping to a maildir.
@node Dired
@section Dired
It is possible to attach files to @t{mu4e} messages using @t{dired}
(@ref{Dired,,emacs}), using the following steps (based on a post on
the @t{mu-discuss} mailing list by @emph{Stephen Eglen}).
@lisp
(add-hook 'dired-mode-hook 'turn-on-gnus-dired-mode)
@end lisp
Then, mark the file(s) in @t{dired} you would like to attach and press
@t{C-c RET C-a}, and you'll be asked whether to attach them to an
existing message, or create a new one.
@node Hydra @node Hydra
@section Hydra @section Hydra
@ -3439,8 +3511,8 @@ With Hydra installed, we can add multi-character shortcuts, for instance:
Now, you can bind a convenient key to my-mu4e-bookmarks/body. Now, you can bind a convenient key to my-mu4e-bookmarks/body.
@end lisp @end lisp
@node Example configs @node Example configurations
@appendix Example configs @appendix Example configurations
In this chapter, we show some example configurations. While it is very useful In this chapter, we show some example configurations. While it is very useful
to see some working settings, we'd like to warn against blindly copying such to see some working settings, we'd like to warn against blindly copying such
@ -3511,7 +3583,7 @@ customize.
mu4e-trash-folder "/trash") mu4e-trash-folder "/trash")
;; the maildirs you use frequently; access them with 'j' ('jump') ;; the maildirs you use frequently; access them with 'j' ('jump')
(setq mu4e-maildir-shortcuts (setq mu4e-maildir-shortcuts
'((:maildir "/archive" :key ?a) '((:maildir "/archive" :key ?a)
(:maildir "/inbox" :key ?i) (:maildir "/inbox" :key ?i)
(:maildir "/work" :key ?w) (:maildir "/work" :key ?w)
@ -3527,6 +3599,10 @@ customize.
(:from . 22) (:from . 22)
(:subject . nil))) ;; alternatively, use :thread-subject (:subject . nil))) ;; alternatively, use :thread-subject
(add-to-list 'mu4e-bookmarks
;; ':favorite t' i.e, use this one for the modeline
'(:query "maildir:/inbox" :name "Inbox" :key ?i :favorite t))
;; program to get mail; alternatives are 'fetchmail', 'getmail' ;; program to get mail; alternatives are 'fetchmail', 'getmail'
;; isync or your own shellscript. called when 'U' is pressed in ;; isync or your own shellscript. called when 'U' is pressed in
;; main view. ;; main view.
@ -3688,6 +3764,10 @@ Next step: let's make a @t{mu4e} configuration for this:
(:maildir "/[Gmail].Trash" :key ?t) (:maildir "/[Gmail].Trash" :key ?t)
(:maildir "/[Gmail].All Mail" :key ?a))) (:maildir "/[Gmail].All Mail" :key ?a)))
(add-to-list 'mu4e-bookmarks
;; ':favorite t' i.e, use this one for the modeline
'(:query "maildir:/inbox" :name "Inbox" :key ?i :favorite t))
;; allow for updating mail using 'U' in the main view: ;; allow for updating mail using 'U' in the main view:
(setq mu4e-get-mail-command "offlineimap") (setq mu4e-get-mail-command "offlineimap")
@ -3801,7 +3881,7 @@ Yes you can --- see the documentation for the variable
@t{mu4e-headers-leave-behavior}. @t{mu4e-headers-leave-behavior}.
@subsection How can I set @t{mu4e} as the default e-mail client in Emacs? @subsection How can I set @t{mu4e} as the default e-mail client in Emacs?
See @ref{Emacs default}. See @ref{Default email client}.
@subsection Can @t{mu4e} use some fancy Unicode instead of these boring plain-ASCII ones? @subsection Can @t{mu4e} use some fancy Unicode instead of these boring plain-ASCII ones?
Glad you asked! Yes, if you set @code{mu4e-use-fancy-chars} to @t{t}, Glad you asked! Yes, if you set @code{mu4e-use-fancy-chars} to @t{t},
@ -4619,7 +4699,7 @@ to provide this information (this is implemented in
@file{mu-cmd-server.c}). @file{mu-cmd-server.c}).
We start this sequence when @t{mu4e} is invoked (when the program is We start this sequence when @t{mu4e} is invoked (when the program is
started). It calls @t{mu4e-server-ping}, and registers a (lambda) started). It calls @t{mu4e--server-ping}, and registers a (lambda)
function for @t{mu4e-server-pong-func}, to handle the response. function for @t{mu4e-server-pong-func}, to handle the response.
@verbatim @verbatim
@ -4667,9 +4747,9 @@ it on all the time. By default, the log only maintains the most recent
@c @unnumbered Variable Index @c @unnumbered Variable Index
@c @printindex vr @c @printindex vr
@c @node Concept Index @node Concept Index
@c @unnumbered Concept Index @unnumbered Concept Index
@c @printindex cp @printindex cp
@bye @bye