summaryrefslogtreecommitdiff
path: root/mu4e/mu4e-bookmarks.el
blob: edecc31a7317e91b6146655c962250ff7de37219 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
;;; mu4e-bookmarks.el --- Bookmarks handling -*- lexical-binding: t -*-

;; Copyright (C) 2011-2026 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:

;;; Code:
(require 'mu4e-helpers)
(require 'mu4e-modeline)
(require 'mu4e-folders)
(require 'mu4e-query-items)

;;; Configuration

(defgroup mu4e-bookmarks nil
  "Settings for bookmarks."
  :group 'mu4e)

(defcustom mu4e-bookmarks
  '(( :name  "Unread messages"
      :query "flag:unread AND NOT flag:trashed"
      :key ?u)
    ( :name "Today's messages"
      :query "date:today..now"
      :key ?t)
    ( :name "Last 7 days"
      :query "date:7d..now"
      :hide-unread t
      :key ?w)
    ( :name "Messages with images"
      :query "mime:image/*"
      :key ?p))
  "List of predefined queries that are shown on the main screen.

Each of the list elements is a plist with at least:
`:name'  - the name of the query
`:query' - the query expression string or function

Likely, you also want to assign a shortcut key;
`:key'   - the shortcut key (single character)

Furthermore, you can add the following:

- `:favorite' - if t, monitor the results of this query, and make
it eligible for showing its status in the modeline. At most
one bookmark should have this set to t (otherwise the _first_
bookmark is the implicit favorite). The query for the `:favorite'
item must be unique among `mu4e-bookmarks' and
`mu4e-maildir-shortcuts'.
- `:hide' - if t, the bookmark is hidden from the main-view and
speedbar.
- `:hide-if-no-unread' - if t, the shortcut is hidden from
   the main-view if it contains no unread messages.

You can also use:
- `: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
numbers shown may differ from the number you get from a normal
query."
  :type '(repeat (plist))
  :group 'mu4e-bookmarks)

(declare-function mu4e-query-items "mu4e-query-items")
(declare-function mu4e--query-item-display-short-counts "mu4e-query-items")

(defun mu4e-ask-bookmark (prompt &optional query-item)
  "Ask user for bookmark using PROMPT.
Return the corresponding query. The bookmark are as defined in
`mu4e-bookmarks'.

If QUERY-ITEM is non-nil, return the full query-item rather than
just the query-string.

The names of the bookmarks are displayed in the minibuffer,
suffixed with the short version of the unread counts, as per
`mu4e--query-item-display-short-counts'."
  (unless (mu4e-bookmarks) (mu4e-error "No bookmarks defined"))
  (let* ((bmarks
          (seq-map
           (lambda (bm) ;; find query-item for bookmark
             (let* ((qitem (seq-find
                           (lambda (qitem)
                             (equal (plist-get bm :query) (plist-get qitem :query)))
                           (mu4e-query-items 'bookmarks)))
                    (unreads (mu4e--query-item-display-short-counts qitem)))
               (cons (format "%c%s%s"
                             (plist-get bm :key)
                             (plist-get bm :name)
                             unreads) bm)))
          (mu4e-filter-single-key (mu4e-bookmarks))))
         (chosen (mu4e-read-option prompt bmarks)))
    ;; return either the query string, or the corresponding query-item.
    (if query-item
        chosen
      (plist-get chosen :query))))

(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))))
    (mu4e--bookmark-query chosen-bm)))

(defun mu4e-bookmark-define (query name key)
  "Define a bookmark for QUERY with NAME and shortcut KEY.
Append it to `mu4e-bookmarks'. Replaces any existing bookmark
with KEY."
  (setq mu4e-bookmarks
        (seq-remove
         (lambda (bm)
           (= (plist-get bm :key) key))
         (mu4e-bookmarks)))
  (cl-pushnew `(:name  ,name
                       :query ,query
                       :key   ,key)
              mu4e-bookmarks :test 'equal))

(defun mu4e-bookmarks ()
  "Get `mu4e-bookmarks' in the (new) format.
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))

(defun mu4e-bookmark-favorite ()
  "Find the favorite bookmark."
  ;; note, use query-items, which will have picked a favorite
  ;; even if user did not provide one explictly
  (seq-find
   (lambda (item)
     (plist-get item :favorite))
   (mu4e-query-items 'bookmarks)))

;; 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-bookmark-favorite))))
    (mu4e-search-bookmark fav)))

(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."
  (when-let* ((fav ;; any results for the favorite bookmark item?
              (seq-find (lambda (bm) (plist-get bm :favorite))
                        (mu4e-query-items 'bookmarks))))
    (cl-destructuring-bind (&key unread count delta-unread
                                 &allow-other-keys) fav
      (propertize
       (format "%s%s "
               (funcall (if mu4e-use-fancy-chars 'cdr 'car)
                        (cond
                         ((> delta-unread 0)  mu4e-modeline-new-items)
                         ((> unread 0) mu4e-modeline-unread-items)
                         ((> count 0) mu4e-modeline-all-read)
                         (t mu4e-modeline-all-clear)))
               (mu4e--query-item-display-counts fav))
       'help-echo
       (format
        (concat
         "mu4e favorite bookmark '%s':\n"
         "\t%s\n\n"
         "number of matches: %d\n"
         "unread messages: %d\n"
         "changes since baseline: %+d\n")
        (plist-get fav :name)
        (mu4e--bookmark-query fav)
        count unread  delta-unread)
       'mouse-face 'mode-line-highlight
       'keymap '(mode-line keymap
                           (mouse-1 . mu4e-jump-to-favorite)
                           (mouse-2 . mu4e-jump-to-favorite)
                           (mouse-3 . mu4e-jump-to-favorite))))))
(provide 'mu4e-bookmarks)
;;; mu4e-bookmarks.el ends here