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.
- 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
difference from that in the main view. See the [[info:mu4e#Bookmarks and Maildirs][manual entry]] for details.
- ~mu4e~ now keeps track of 'baseline' query results and shows the difference
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
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
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~
installed, you can do e.g.,
@ -92,7 +101,7 @@
to get a histogram of such messages. Note, this area is under active
developement and will likely change.
*** building
*** building and installation
- 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~.
@ -339,8 +348,12 @@
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
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
~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)
*** mu
@ -939,7 +949,7 @@ End of search results
- org: improve template keywords
- rework URL handling
** Release 0.9.5
** Release 0.9.10
*** mu

View File

@ -98,6 +98,7 @@ struct Server::Private {
void move_handler(const Command& cmd);
void mkdir_handler(const Command& cmd);
void ping_handler(const Command& cmd);
void queries_handler(const Command& cmd);
void quit_handler(const Command& cmd);
void remove_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); }});
cmap.emplace(
"ping",
CommandInfo{
ArgMap{},
"ping the mu-server and get server information in the response",
[&](const auto& params) { ping_handler(params); }});
cmap.emplace(
"queries",
CommandInfo{
ArgMap{
{":queries",
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",
[&](const auto& params) { ping_handler(params); }});
"get unread/totals information for a list of queries",
[&](const auto& params) { queries_handler(params); }});
cmap.emplace("quit", CommandInfo{{}, "quit the mu server", [&](const auto& params) {
quit_handler(params);
@ -916,9 +920,28 @@ Server::Private::ping_handler(const Command& cmd)
const auto storecount{store().size()};
if (storecount == (unsigned)-1)
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")
.value_or(std::vector<std::string>{})};
Sexp qresults;
for (auto&& q : queries) {
const auto count{store_.count_query(q)};
@ -929,22 +952,10 @@ Server::Private::ping_handler(const Command& cmd)
":unread", unread));
}
Sexp addrs;
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)));
output_sexp(Sexp(":queries"_sym, std::move(qresults)));
}
void
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())};
if (cmit == cmap_.cend())
return Err(Error::Code::Command,
"unknown command in command '%s'",
cmd.to_string().c_str());
"unknown command '%s'", cmd.to_string().c_str());
const auto& cmd_info{cmit->second};
if (do_validate) {

View File

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

View File

@ -1,6 +1,6 @@
;;; 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>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -24,6 +24,8 @@
;;; Code:
(require 'mu4e-helpers)
(require 'mu4e-server)
(require 'mu4e-modeline)
;;; Configuration
@ -50,19 +52,24 @@
Each of the list elements is a plist with at least:
`:name' - the name of the query
`:query' - the query expression or function
`:key' - the shortcut key.
`:query' - the query expression string (not a function)
`:key' - the shortcut key (single character)
Note that the :query parameter can be a function/lambda.
Optionally, you can add the following: `: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.
Optionally, you can add the following:
`:hide-unread' is implied from `:hide'. Furthermore, it is
implied when `:query' is a function.
- `:favorite' - if t, monitor the results of this query, and make
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
counts do not discard duplicate or unreadable messages. Thus, the
@ -72,7 +79,7 @@ query."
:group 'mu4e-bookmarks)
(defun mu4e-ask-bookmark (prompt)
(defun mu4e-ask-bookmark (prompt)
"Ask the user for a bookmark (using PROMPT) as defined in
`mu4e-bookmarks', then return the corresponding query."
(unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined"))
@ -88,23 +95,32 @@ query."
(kar (read-char (concat prompt bmarks))))
(mu4e-get-bookmark-query kar)))
(defun mu4e--bookmark-query (bm)
"Get query string for some bookmark."
(when bm
(let* ((query (or (plist-get bm :query)
(mu4e-warn "No query in %S" bm)))
;; queries being functions is deprecated.
(query (if (functionp query) (funcall query) query)))
;; earlier, we allowed for the queries being fucntions
(unless (stringp query)
(mu4e-warn "Could not get query string from %s" bm))
;; apparently, non-UTF8 queries exist, i.e.,
;; with maild dir names.
(decode-coding-string query 'utf-8 t))))
(defun mu4e-get-bookmark-query (kar)
"Get the corresponding bookmarked query for shortcut KAR.
Raise an error if none is found."
(let* ((chosen-bm
(or (seq-find
(lambda (bm)
(= kar (plist-get bm :key)))
(mu4e-bookmarks))
(mu4e-warn "Unknown shortcut '%c'" kar)))
(expr (plist-get chosen-bm :query))
(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))))
(let ((chosen-bm
(or (seq-find
(lambda (bm)
(= kar (plist-get bm :key)))
(mu4e-bookmarks))
(mu4e-warn "Unknown shortcut '%c'" kar))))
(mu4e--bookmark-query chosen-bm)))
(defun mu4e-bookmark-define (query name key)
"Define a bookmark for QUERY with NAME and shortcut KEY.
@ -116,8 +132,8 @@ with KEY."
(= (plist-get bm :key) key))
(mu4e-bookmarks)))
(cl-pushnew `(:name ,name
:query ,query
:key ,key)
:query ,query
:key ,key)
mu4e-bookmarks :test 'equal))
(defun mu4e-bookmarks ()
@ -125,9 +141,159 @@ with KEY."
Convert from the old format if needed."
(seq-map (lambda (item)
(if (and (listp item) (= (length item) 3))
`(:name ,(nth 1 item) :query ,(nth 0 item)
:key ,(nth 2 item))
item)) mu4e-bookmarks))
`(:name ,(nth 1 item) :query ,(nth 0 item)
:key ,(nth 2 item))
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)
;;; 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 -*-
;; Copyright (C) 2015-2022 Dirk-Jan C. Binnema
;; Copyright (C) 2015-2023 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
@ -28,6 +28,7 @@
;;; Code:
(require 'mu4e-helpers)
(require 'mu4e-modeline)
;;; Configuration
@ -82,14 +83,6 @@ none."
(if ctx (mu4e-context-name ctx) "<none>")))
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
"A mu4e context object with the following members:
- `name': the name of the context, eg. \"Work\" or \"Private\".
@ -153,8 +146,7 @@ non-nil."
(setq mu4e--context-current context)
(run-hooks 'mu4e-context-changed-hook)
(mu4e-message "Switched context to %s" (mu4e-context-name context))
(force-mode-line-update))
(mu4e-message "Switched context to %s" (mu4e-context-name context)))
context))
(defun mu4e--context-autoswitch (&optional msg policy)
@ -204,13 +196,6 @@ as it is."
(mu4e--context-ask-user "Select context: ")))
(_ 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)
"Evaluate BODY, with variables let-bound for CONTEXT (if any).
`funcall'."
@ -221,6 +206,14 @@ global-mode-line."
(mapcar (lambda(cell) (cdr cell)) vars)
(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
"Mode for switching the mu4e context."
:global nil
@ -231,7 +224,7 @@ global-mode-line."
(define-key map (kbd ";") #'mu4e-context-switch)
map)
:lighter ""
(mu4e-context-in-modeline))
(mu4e--modeline-register #'mu4e--context-modeline-item))
;;;
(provide 'mu4e-context)

View File

@ -1,6 +1,6 @@
;;; 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>
;; 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)))
(setq mu4e--search-last-query 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,
;; switch to it.
@ -1178,7 +1178,10 @@ The following specs are supported:
(mu4e-context-minor-mode)
(mu4e-update-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
@ -1244,7 +1247,7 @@ message plist, or nil if not found."
;;; Queries & searching
(defvar mu4e~headers-mode-line-label "")
(defun mu4e~headers-update-mode-line ()
(defun mu4e~headers-modeline-item ()
"Update mode-line settings."
(let* ((flagstr
(mapconcat
@ -1258,30 +1261,8 @@ message plist, or nil if not found."
(,mu4e-search-skip-duplicates
. ,mu4e-headers-skip-duplicates-label)
(,mu4e-search-hide-enabled . ,mu4e-headers-hide-label))
""))
(name "mu4e-headers"))
(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)
""))))))
"")))
(concat flagstr " " mu4e--search-last-query)))
;;; Search-based marking

View File

@ -65,11 +65,6 @@ You can customize the exact fancy characters used with
:type 'boolean
: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
;; here because they're needed in multiple buffers.
@ -234,101 +229,6 @@ Function returns the cdr of the list element."
(cdr chosen)
(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

View File

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

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
'mu4e-search-read-query "1.7.0")
(make-obsolete-variable 'mu4e-display-update-status-in-modeline
"No longer used" "1.9.11")
;; mu4e-headers
(make-obsolete-variable 'mu4e-headers-field-properties-function

View File

@ -165,8 +165,7 @@ but also manually invoked searches."
;;; History
(defvar mu4e--search-query-past nil
"Stack of queries before the present one.")
(defvar mu4e--search-query-future nil
"Stack of queries after the present one.")
(defvar mu4e--search-query-future nil "Stack of queries after the present one.")
(defvar mu4e--search-query-stack-size 20
"Maximum size for the query stacks.")
(defvar mu4e--search-last-query nil
@ -217,9 +216,14 @@ show the message with MSGID."
If EDIT is non-nil, let the user edit the bookmark before starting
the search."
(interactive)
(let ((expr
(let* ((expr
(or expr
(mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: ")))))
(mu4e-ask-bookmark (if edit "Select bookmark: " "Bookmark: "))))
(fav (mu4e--bookmark-query (mu4e-favorite-bookmark))))
;; reset baseline when searching for bookmark query
(when (and fav (string= fav expr))
(mu4e--reset-baseline))
(run-hook-with-args 'mu4e-search-bookmark-hook expr)
(mu4e-search expr (when edit "Edit bookmark: ") edit)))

View File

@ -132,12 +132,55 @@ from the server process.")
(defvar mu4e-pong-func nil
"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
"A function called for each (:contacts (<list-of-contacts>))
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
"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."
(and mu4e--server-process
(memq (process-status mu4e--server-process)
'(run open listen connect stop))
t))
'(run open listen connect stop)) t))
(defsubst mu4e--server-eat-sexp-from-buf ()
"'Eat' the next s-expression from `mu4e--server-buf'.
@ -286,9 +328,14 @@ The server output is as follows:
;; received a pong message
((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))
;; 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
;; note: we use 'member', to match (:contacts nil)
((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)
:no-view ,(and no-view t))))
(defun mu4e--server-ping (&optional queries)
"Sends a ping to the mu server, expecting a (:pong ...) in response.
(defun mu4e--server-ping ()
"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
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)
"Remove message with DOCID.

View File

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

View File

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

View File

@ -12,7 +12,7 @@
@c %**end of header
@copying
Copyright @copyright{} 2012-2022 Dirk-Jan C. Binnema
Copyright @copyright{} 2012-2023 Dirk-Jan C. Binnema
@quotation
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.
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}.
@menu
@ -99,25 +99,24 @@ with answers to frequently asked questions, @ref{FAQ}.
Appendices
* 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
* Tips and Tricks:: Useful tips
* How it works:: Some notes about the implementation of @t{mu4e}
* Debugging:: How to debug problems in @t{mu4e}
* GNU Free Documentation License:: The license of this manual
@c Indices
Indices
@c * Command Index:: An item for each standard command name.
@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
@node Introduction
@chapter Introduction
Let's get started!
Let's get started!q
@menu
* Why another e-mail client::Aren't there enough already
* 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
that.
@subsection Installing
@subsection Installation
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
@ -337,7 +336,7 @@ configuration before trying again:
There is some support for using the Emacs customization system in
@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.
@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}
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:
@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.
Here, we only provide some simple examples --- for more, see
@ref{Example configs}.
@ref{Example configurations}.
A very minimal setup:
@ -716,18 +715,18 @@ The main view looks something like the following:
Bookmarks
* [bu] Unread messages (13085/+3/13085)
* [bu] Unread messages (13085(+3)/13085)
* [bt] Today's messages
* [bw] Last 7 days (53/+3/128)
* [bp] Messages with images (75/-2/2441)
* [bw] Last 7 days (53(+3)/128)
* [bp] Messages with images (75/2441)
Maildirs
* [ja] /archive (2101/0/18837)
* [ji] /inbox (8/+2/10)
* [jb] /bulk (33/+0/35)
* [jB] /bulkarchive (179/+0/2090)
* [jm] /mu (694/+1/17687)
* [ja] /archive (2101/18837)
* [ji] /inbox (8(+2)/10)
* [jb] /bulk (33/35)
* [jB] /bulkarchive (179/2090)
* [jm] /mu (694(+1)/17687)
* [jn] /sauron
* [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}.
Bookmarks are predefined queries with a descriptive name and a
shortcut --- in the example above, we see the default bookmarks. You
can view the list of messages matching a certain bookmark by pressing
@key{b} followed by the bookmark's shortcut. If you'd like to edit the
bookmarked query first before invoking it, use @key{B}.
Bookmarks are predefined queries with a descriptive name and a shortcut --- in
the example above, we see the default bookmarks. You can view the list of
messages matching a certain bookmark by pressing @key{b} followed by the
bookmark's shortcut. If you want to edit the bookmarked query before invoking
it, use @key{B}.
Next to each bookmark there is the number of (unread/delta/all) messages that
match.
Next to each bookmark are some numbers that indicate the (unread(delta)/all)
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
difference in unread messages since ``baseline'', which is the state at some
point in the past. This delta is useful to quickly see what changed since the
last time you looked.
Note that the ``delta'' has its limitations: if you, for instance, deleted 5
messages and received 5 new one, the ``delta'' would be 0, although there were
changes indeed. So it is mostly useful for tracking changes while you are
@emph{not} using @t{mu4e}.
By default, the baseline is automatically reset when switching to the main view
explicitly; otherwise the baseline can be reset with
@code{mu4e-reset-baseline-query-results}, which is bound to @kbd{R} in the
main-view, and @kbd{C-c C-R} throughout @t{mu4e}. You can customize the behavior
using @var{mu4e-main-hide-baseline-delta} and
@var{mu4e-main-auto-reset-baseline}.
This delta is useful to quickly see that there are new messages since the last
time you looked. Imagine switching to another buffer to work on something or
even (!) stepping away from your computer to return later: the baseline allows
you to quickly see what changed.
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
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"))
@end lisp
There are optional keys @t{:hide} to hide the bookmark from the main
menu, but still have it available (using @key{b})) and
@t{:hide-unread} to avoid generating the unread-number; that can be
useful if you have bookmarks for slow queries. Note that
@t{:hide-unread} is implied when the query is not a string; this for
the common case where the query function involves some user input,
There are optional keys @t{:hide} to hide the bookmark from the main menu, but
still have it available (using @key{b})) and @t{:hide-unread} to avoid
generating the unread-number; that can be useful if you have bookmarks for slow
queries. Note that @t{:hide-unread} is implied when the query is not a string;
this for the common case where the query function involves some user input,
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
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.
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
results.
@ -1740,7 +1746,7 @@ then still include the signature manually, using the function
@itemize
@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
to kill the buffer instead, add something like the following to your
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)}.
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
@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,
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
@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}.
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
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
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}.
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
first character of this string determines its shortcut, so these should be
unique. If necessary, simply prefix the name with a unique character.
@item a predicate function, taking two arguments @var{msg} and @var{param}.
@var{msg} is the message plist (see @ref{Message functions}) and @var{param} is
@item a predicate function, taking two arguments @code{msg} and @code{param}.
@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
item). The predicate function should return non-@t{nil} if the message
matches.
@ -2575,8 +2581,8 @@ when starting; see the discussion in the previous section.
A couple of notes about this example:
@itemize
@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.
The current context appears in the mode-line.
by default bound to @kbd{;} in headers, view and main mode. The current context
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
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
@ -2713,11 +2719,10 @@ work-email. You can achieve this with something like:
@noindent
Good to remember:
@itemize
@item The @var{msg} parameter you receive in the function refers to the
@emph{original message}, that is, the message being replied to or
forwarded. When re-editing a message, it refers to the message being
edited. When you compose a totally new message, the @var{msg} parameter is
@code{nil}.
@item The @code{msg} parameter you receive in the function refers to the
@emph{original message}, that is, the message being replied to or forwarded.
When re-editing a message, it refers to the message being edited. When you
compose a totally new message, the @code{msg} parameter is @code{nil}.
@item When re-editing messages, the value of @code{mu4e-drafts-folder} is ignored.
@end itemize
@ -3052,32 +3057,84 @@ see @code{mu4e-toggle-logging}.
@code{user-error} and @code{error} functions.
@end itemize
@node Other tools
@appendix Other tools
@node Integrating with Emacs
@chapter Integrating with Emacs
In this chapter, we discuss some ways in which @t{mu4e} can cooperate
with other tools.
In this chapter, we discuss how you can integrate @t{mu4e} with Emacs in various
ways. Here we focus on Emacs built-ins; for dealing with external tools,
@xref{Other tools}.
@menu
* Emacs default::Making mu4e the default emacs e-mail program
* Emacs bookmarks::Using Emacs' bookmark system
* Modeline::Showing mu4e's status in the modeline
* 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-contacts::Hooking up with org-contacts
* BBDB::Hooking up with the Insidious Big Brother Database
* iCalendar::Enabling iCalendar invite processing
* Sauron::Getting new mail notifications with Sauron
* Speedbar::A special frame with your folders
* Dired:: Attaching files using @t{dired}
* Hydra:: Custom shortcut menus
@end menu
@node Emacs default
@section Emacs default
@node Modeline
@section Modeline
@cindex modeline
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:
One of the most visible ways in which @t{mu4e} integrates with Emacs is through
the @emph{modeline} @xref{Mode Line,,,emacs}. The @t{mu4e} support for that is
handled through a minor-mode @code{mu4e-modeline-mode}, which is enabled by
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
(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})
@node Using bookmarks
@section Using bookmarks
@node Emacs bookmarks
@section Emacs bookmarks
@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.
Note, emacs bookmarks are not to be confused with mu4e's bookmarks; the former
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.
@node 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)
@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
@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
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
@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
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
@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.
@end lisp
@node Example configs
@appendix Example configs
@node Example configurations
@appendix Example configurations
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
@ -3511,7 +3583,7 @@ customize.
mu4e-trash-folder "/trash")
;; the maildirs you use frequently; access them with 'j' ('jump')
(setq mu4e-maildir-shortcuts
(setq mu4e-maildir-shortcuts
'((:maildir "/archive" :key ?a)
(:maildir "/inbox" :key ?i)
(:maildir "/work" :key ?w)
@ -3527,6 +3599,10 @@ customize.
(:from . 22)
(: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'
;; isync or your own shellscript. called when 'U' is pressed in
;; main view.
@ -3688,6 +3764,10 @@ Next step: let's make a @t{mu4e} configuration for this:
(:maildir "/[Gmail].Trash" :key ?t)
(: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:
(setq mu4e-get-mail-command "offlineimap")
@ -3801,7 +3881,7 @@ Yes you can --- see the documentation for the variable
@t{mu4e-headers-leave-behavior}.
@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?
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}).
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.
@verbatim
@ -4667,9 +4747,9 @@ it on all the time. By default, the log only maintains the most recent
@c @unnumbered Variable Index
@c @printindex vr
@c @node Concept Index
@c @unnumbered Concept Index
@c @printindex cp
@node Concept Index
@unnumbered Concept Index
@printindex cp
@bye