nftable-migration
1#+include: ~/OrgFiles/armin/org-macros.setup
2#+OPTIONS: h:1 num:nil toc:nil d:nil
3
4#+TITLE: consult-mu - use consult to search mu4e dynamically or asynchronously
5#+AUTHOR: Armin Darvish
6#+LANGUAGE: en
7
8#+html: <a href="https://github.com/armindarvish"><img alt="Armin Darvish" src="https://img.shields.io/static/v1?label=Armin%20Darvish&message=consult-mu&color=00A8B0&logo=github"/></a>
9#+html: <a href="https://www.gnu.org/software/emacs/"><img alt="GNU Emacs" src="https://img.shields.io/static/v1?label=Made%20For&message=GNU%20Emacs&color=7a5aba&logo=gnuemacs&logoColor=white"/></a>
10
11* About consult-mu
12Consult-mu provides a dynamically updated search interface to mu4e. It uses the awesome package [[https://github.com/minad/consult][consult]] by [[https://github.com/minad][Daniel Mendler]] and [[https://github.com/djcb/mu][mu/mu4e]] by [[https://github.com/djcb/][Dirk-Jan C. Binnema,]] and optionally [[https://github.com/oantolin/embark][Embark]] by [[https://github.com/oantolin][Omar AntolĂn Camarena]] to improve the search experience of mu4e.
13
14** Main Interactive Commands
15 There two main interactive commands:
16
171. =consult-mu-dynamic=: Provides a dynamic version of =mu4e-search=. As the user types inputs, the result gets updated. This command uses a modified version of =mu4e-search= and then takes the content of =mu4e-headers= buffer to populate minibuffer completion table. This allows the user to change the query or search properties (such as number of results, sort-field, sort-direction, ...) dynamically by changing the input in the minibuffer. In addition previews of the results can be viewed similar to other consult functions. Once a candidate is selected, the user will see the search result sin =mu4e-headers=, and =mu4e-view= buffers similar to mu4e-search results.
18
192. =consult-mu-async=: This function provides a very fast search without loading mu4e-headers, which means mu4e functionalities (like marks, reply, forwards, etc.) are not available in the preview buffer. This is very useful for finding individual emails or threads in a large pool quickly (in other words "a needle in a haystack" scenarios!). Previews can be seen while the results are being populated asynchronously (without populating mu4e-headers buffer). Upon selection of a candidate, mu4e-headers buffer is populated with only an individual email (or thread). From here all the normal functionalities of mu4e is again available.
20
213. =consult-mu=: This is simply a wrapper that calls =consult-mu-default-command=, which can be set to either the dynamic or async command for quick access. In other words, set =consult-mu-default-command= to either 1 or 2 above depending on your use case and then use =consult-mu= for convinience and you don't need to remember the difference between the two anymore.
22
23The advantage of =consult-mu-async= over =consult-mu-dynamic= is that it is very fast for searching several thousands of messages (even faster than [[https://github.com/emacsmirror/consult-notmuch][consult-notmuch]]!), but cannot populate a mu4e-headers buffer with all the results. On the other hand, =consult-mu-dynamic= is slower when there are thousands of hits for the search term but provides full functionality for all the results (provides full functionality of =mu4e-search=). Therefore, depending on the use case, the user can chose which functions serves the purpose better.
24
25Furthermore, =consult-mu=, also provides a number of useful [[https://github.com/oantolin/embark][Embark]] actions that can be called from within minibuffer (see examples below). However, when using embark actions, be advised that sometimes you may get an error especially when using =consult-mu-async=. These are likely not critical errors hapening when the databse is out of sync with the results. In such cases syncing the database should resolve the issue.
26
27
28* Getting Started
29** Installation
30Before you start, make sure you understand that this is work in progress in its early stage and bugs and breaks are very much expected.
31
32*note that*: Because [[https://github.com/djcb/mu][mu4e]] tends to take over buffer/windows management, I had to reimplement (a.k.a. hack) some of the functionalities in order to provide quick previews that stay out of your way when the minibuffer command is done (or canceled), and as a result there is a good chance that errors will arise in edge cases that I have not tested.
33
34*** Requirements
35In order to use consult-mu, you need the following requirements:
36
37**** [[https://github.com/djcb/mu][mu4e]]:
38
39You can access the official documentation for mu4e here: [[https://www.djcbsoftware.nl/code/mu/mu4e/][mu4e official manual]]. If you need step-by-step instructions or prefer videos, there are many useful tutorials online. Here are a few good links:
40
41- EmacsWiki: [[https://www.emacswiki.org/emacs/mu4e][EmacsWiki: mu4e]]
42- SystemCrafters Videos: [[https://www.youtube.com/watch?v=yZRyEhi4y44][Streamline Your E-mail Management with mu4e - Emacs Mail - YouTube]] & [[https://www.youtube.com/watch?v=olXpfaSnf0o][Managing Multiple Email Accounts with mu4e and mbsync - Emacs Mail - YouTube]]
43- Setting Mu4e on MacOS: [[https://macowners.club/posts/email-emacs-mu4e-macos/][Email setup in Emacs with Mu4e on macOS | macOS & (open-source) Software]]
44
45**** [[https://github.com/minad/consult][consult]]:
46
47To install consult follow the official instructions here: [[https://github.com/minad/consult#configuration][Configuration of Consult.]]
48
49Also, make sure you review Consult's README since it recommends some other packages and useful configurations for different settings. Some of those may improve your experience of consult-mu as well. In particular, the section about [[https://github.com/minad/consult#asynchronous-search][asynchronous search]] is important for learning how to use inputs to search for result and narrow down in minibuffer.
50
51
52*** Installing consult-mu package
53consult-mu is not currently on [[https://elpa.gnu.org/packages/consult.html][ELPA]] or [[https://melpa.org/#/consult][MELPA]]. Therefore, you need to install it using an alternative non-standard package manager such as [[https://github.com/radian-software/straight.el][straight.el]] or use manual installation.
54
55**** straight.el
56To install consult-mu with straight.el you can use the following command. Make sure you load consult-mu after loading mu4e and consult (e.g. =require consult=, =require mu4e=).
57
58#+begin_src emacs-lisp
59(straight-use-package
60 '(consult-mu :type git :host github :repo "armindarvish/consult-mu" :branch "main" :files (:defaults "extras/*.el")))
61#+end_src
62
63or if you use =use-package= macro with straight, you can do:
64
65#+begin_src emacs-lisp
66(use-package consult-mu
67 :straight (consult-mu :type git :host github :repo "armindarvish/consult-mu" :files (:defaults "extras/*.el"))
68 :after (mu4e consult)
69)
70#+end_src
71
72You can also fork this repository and use your own repo.
73
74**** manual installation
75Clone this repo and make sure the files are on your load path, as described on [[https://www.emacswiki.org/emacs/LoadPath][EmacsWiki]].
76
77Make sure you load consult and mu4e (e.g. =require consult=, =require mu4e=) before you load consult-mu.
78
79** Configuration
80consult-mu is built with the idea that the user should be able to customize everything based on their use-case, therefore the user is very much expected to configure consult-mu.
81
82I recommend you read through this section and understand how to configure the package according to your needs and for your specific use-case, but if you just want a drop-in minimal config, look at the snippet below (for snippet with extended settings see [[id:99667231-6F96-4913-834F-7C031E3CC44C][extended feature config]]).
83
84*** Minimal Config
85#+begin_src emacs-lisp
86(use-package consult-mu
87 :straight (consult-mu :type git :host github :repo "armindarvish/consult-mu" :branch "main")
88 :after (consult mu4e)
89 :custom
90 ;;maximum number of results shown in minibuffer
91 (consult-mu-maxnum 200)
92 ;;show preview when pressing any keys
93 (consult-mu-preview-key 'any)
94 ;;do not mark email as read when previewed
95 (consult-mu-mark-previewed-as-read nil)
96 ;;do not amrk email as read when selected. This is a good starting point to ensure you would not miss important emails marked as read by mistake especially when trying this package out. Later you can change this to t.
97 (consult-mu-mark-viewed-as-read nil)
98 ;; open the message in mu4e-view-buffer when selected.
99 (consult-mu-action #'consult-mu--view-action)
100 )
101#+end_src
102
103*** Extended Feature Config
104:PROPERTIES:
105:ID: 99667231-6F96-4913-834F-7C031E3CC44C
106:END:
107
108Here is a customization that gives you the full feature experience including utilities for attaching/detaching files and searching contacts, etc.
109
110#+begin_src emacs-lisp
111(use-package consult-mu
112 :straight (consult-mu :type git :host github :repo "armindarvish/consult-mu" :branch "develop" :files (:defaults "extras/*.el"))
113 :after (consult mu4e)
114 :custom
115 ;;maximum number of results shown in minibuffer
116 (consult-mu-maxnum 200)
117 ;;show preview when pressing any keys
118 (consult-mu-preview-key 'any)
119 ;;do not mark email as read when previewed. If you turn this to t, be aware that the auto-loaded preview if the preview-key above is 'any would also get marked as read!
120 (consult-mu-mark-previewed-as-read nil)
121 ;;mark email as read when selected.
122 (consult-mu-mark-viewed-as-read t)
123 ;;use reply to all when composing reply emails
124 (consult-mu-use-wide-reply t)
125 ;; define a template for headers view in minibuffer. The example below adjusts the width based on the width of the screen.
126 (consult-mu-headers-template (lambda () (concat "%f" (number-to-string (floor (* (frame-width) 0.15))) "%s" (number-to-string (floor (* (frame-width) 0.5))) "%d13" "%g" "%x")))
127
128 :config
129 ;;create a list of saved searches for quick access using `histroy-next-element' with `M-n' in minibuffer. Note the "#" character at the beginning of each query! Change these according to
130 (setq consult-mu-saved-searches-dynamics '("#flag:unread"))
131 (setq consult-mu-saved-searches-async '("#flag:unread"))
132 ;; require embark actions for marking, replying, forwarding, etc. directly from minibuffer
133 (require 'consult-mu-embark)
134 ;; require extra module for composing (e.g. for interactive attachment) as well as embark actions
135 (require 'consult-mu-compose)
136 (require 'consult-mu-compose-embark)
137 ;; require extra module for searching contacts and runing embark actions on contacts
138 (require 'consult-mu-contacts)
139 (require 'consult-mu-contacts-embark)
140 ;; change the prefiew key for compose so you don't open a preview of every file when selecting files to attach
141 (setq consult-mu-compose-preview-key "M-o")
142 ;; pick a key to bind to consult-mu-compose-attach in embark-file-map
143 (setq consult-mu-embark-attach-file-key "C-a")
144 (setq consult-mu-contacts-ignore-list '("^.*no.*reply.*"))
145 (setq consult-mu-contacts-ignore-case-fold-search t)
146 (consult-mu-compose-embark-bind-attach-file-key)
147 ;; choose if you want to use dired for attaching files (choice of 'always, 'in-dired, or nil)
148 (setq consult-mu-compose-use-dired-attachment 'in-dired)
149 )
150
151#+end_src
152
153*** Customization Variables
154The following customizable variables are provided:
155
156**** main
157***** =consult-mu-default-command=
158A command function that is called when =M-x consult-mu= is called. This is useful for defining special conditions to use =consult-mu-dynamics= or =consult-mu-async=. By default it is bound to =consult-mu-dynamics=, therefore =M-x consult-mu= simply calls =consult-mu-dynamics=.
159
160***** =consult-mu-headers-buffer-name=
161This is the default name for HEADERS buffer explicitly for consult-mu. It is, by default, set to ="**consult-mu-headers**"=. Note that currently, the header-buffer name is a constant string and shared between all instances of consult-mu calls. This is to prevent running operations in parallel that cause out-of-sync issues.
162
163***** =consult-mu-view-buffer-name=
164This is the default name for VIEW buffer explicitly for consult-mu. It is, by default, set to ="**consult-mu-view**"=. Note that currently, the view-buffer name is a constant string and shared between all instances of consult-mu calls. This is to prevent creating too many preview buffers and executing operations (such as marking) in parallel that cause out-of-sync issues.
165
166***** =consult-mu-args=
167This is the default name of the =mu= command line argument. It is set to ="mu"= by default, but can be modified for example if mu is at a different path on your system.
168
169***** =consult-mu-maxnum=
170Maximum number of messages shown in search results (in consult-mu minibuffer completion table). This is a global option for consult-mu and consult-mu-async, but can be overriden by providing command line arguments in input for example, the following search would fetch up to 1000 results
171
172#+begin_example
173#github -- --maxnum 1000
174#+end_example
175
176***** =consult-mu-headers-fields=
177This variable is used to format the headers inside minibuffer. This takes a similar format to =mu4e-headers-fields= and changes the format of the header only in minibuffer (=consult-mu-dynamic= or =consult-mu-async=).
178
179Note that it is generally recommended to use =consult-mu-headers-template= below because it has more options and does not affect the consult-mu-headers buffer.
180
181***** =consult-mu-headers-template=
182:PROPERTIES:
183:ID: 155CF359-63D0-4D4D-B0BD-7C089E2D9B3C
184:END:
185This is a template string that overrides the consult-mu-headers-field to format headers. It provides more options than =consult-mu-headers-field= and is generally the recommended approach to make custom headers.
186Special chaacters in the string (either “%[char]" or “%[char][integer]”) in the string get expanded to create headers. Each character represents a different field and the integer defines the length of the field. For exmaple "%d15%s50" means 15 characters for date and 50 charcters for subject.
187
188The list of available fields are:
189
190 %f sender(s) (e.g. from: field of email)
191 %t receivers(s) (i.e. to: field of email)
192 %s subject (i.e. title of email)
193 %d date (i.e. the date email was sent/received) with the format "Thu 09 Nov 23"
194 %p priority
195 %z size
196 %i message-id (as defined by mu)
197 %g flags (as defined by mu)
198 %G pretty flags (this uses mu4e~headers-flags-str to pretify flags)
199 %x tags (as defined by mu)
200 %c cc (i.e. cc: field of the email)
201 %h bcc (i.e. bcc: field of the email)
202 %r date chaged (as defined by :changed in mu4e)
203
204For example, the string ="%d13%s50%f17%G"= would make a header containing =13= characters for =date=, =50= characters for =subject=, and =20= characters for =from= field, making headers that looks like this:
205#+attr_org: :width 800px :height nilpx
206#+attr_latex: :width 800px :height nilpx
207#+attr_html: :width 800px :height nilpx
208[[https://github.com/armindarvish/consult-mu/blob/screenshots/screenshots/consult-mu-headers-template.png]]
209
210***** =consult-mu-search-sort-field=
211This defines the field that is used for sorting the results (refer to documentation on the variable =mu4e-search-sort-field= for more info). It has to be one of the keywords:
212- =:date= sort by date
213- =:subject= sort by title of the email
214- =:size= sort by file size
215- =:prio= sort by priority
216- =:from= sort by name/email of the sender(s)
217- =:to= sort by name/email of receivers
218- =:list= sort by mailing list
219
220
221Note that the sort field can dynamically be changed by providing command line arguments in the minibuffer input.
222
223For example the following input in the minibuffer will search for emails that are flagged unread but then overrides the sort field and change it t =subject=. For details on how to use command line arguments refer to mu manual (e.g. by running =mu find --help= in the command line)
224
225#+begin_example
226#flag:unread -- -s s
227#+end_example
228
229***** =consult-mu-search-sort-direction=
230Direction of sort. It can either be ='ascending= for A->Z (low number to high number) or ='descending= for Z->A (high number to low number). Note that if a command line argument for reverse order (either -z or --reverse) is provided in the minibuffer, the order will be reverse of the setting defined by this variable.
231
232For example, if =consult-mu-search-sort-field= is set to =:date= and =consult-mu-search-sort-direction= is set to ='descending= the messages are sortes chronologically from the newest on top to the oldest. Then providing a reverse order argument in the minibuffer can dynamically reverse the sort direction:
233
234For example the following input in the minibuffer searches for all =unread= emails under ="./inbox"= path then *reverses the sort direction* (because of =-z= command line argument)
235#+begin_example
236#(maildir:/inbox) AND flag:unread -- -z
237#+end_example
238
239
240***** =consult-mu-search-threads=
241This variable determines whether threads are calculated for search results or not similar to the =mu4e-search-threads= variable.
242
243Note that per mu4e docs:
244When threading is enabled, the headers are exclusively sorted chronologically (:date) by the newest message in the thread.
245
246When this variable is set to nil, it can still be truned on by adding command line arguments (i.e. =-t= or =--thread=) in the input. For example the following input in the minibuffer will ensure that threads are on even if =consult-mu-search-threads= is set to nil.
247
248#+begin_example
249#flag:unread -- -t
250#+end_example
251
252***** =consult-mu-group-by=
253This variable determines what field is used to group messages. This aloows quick movement between groups (For example with =vertico-next-group= if you use [[https://github.com/minad/vertico][vertico]])
254
255By default it is set to :date. But can be any of the following keywords:
256
257 - =:subject= group by mail title
258 - =:from= group by name/email of the sender(s)
259 - =:to= group by name/email of the receiver(s)
260 - =:date= group by date in the format "Thu 09 Nov 23"
261 - =:time= group by the time of email in the format "20:30:07"
262 - =:datetime= group by date and time of the email with the format "2023-11-04 08:30:07 PM"
263 - =:year= group by the year of the email (i.e. 2023, 2022, ...)
264 - =:month= group by the month of the email (i.e. Jan, Feb, ..., Dec)
265 - =:week= group by the week number of the email (.i.e. 1, 2, 3, ..., 52)
266 - =:day-of-week= group by the day email was sent (i.e. Monday, Tuesday, ...)
267 - =:size= group by the file size of the email
268 - =:flags= group by flags (as defined by mu)
269 - =:tags= group by tags (as defined by mu)
270 - =:changed= group by the date changed (as defined by :changed field in mu4e)
271
272Note that grouping works alongside sorting. For example if =consult-mu-group-by= is set to =:day-of-week= and =consult-mu-search-sort-field= is set to =:date=, then the messages are grouped by day of week (e.g. all emails on Tuesdays will be in one group) then ordered chronologically within each group. The screenshot below shows some examples:
273#+attr_org: :width 800px :height nilpx
274#+attr_latex: :width 800px :height nilpx
275#+attr_html: :width 800px :height nilpx
276[[https://github.com/armindarvish/consult-mu/blob/screenshots/screenshots/consult-mu-grouping-example.png]]
277
278***** =consult-mu-mark-previewed-as-read=
279This determines whether a message is marked as =read= when it is simply previewed (see =consult-mu-preview-key= above and documentation on =consult-preview-key=).
280
281Note that when =consult-mu-preview-key= is set to ='any=, then as soon as =consult-mu= or =consult-mu-async= retrieve some results a preview for the first message is shown and this can be marked as read if =consult-mu-mark-previewed-as-read= is set to t.
282
283***** =consult-mu-mark-viewed-as-read=
284This determines whether a message is marked as =read= when it is viewed (i.e. when the minibuffer candidate is selected by hitting =RET=).
285
286***** =consult-mu-preview-key=
287This is similar to =consult-preview-key= but only for =consult-mu=. By default, it is set to the value of consult-preview-key to keep consistent experience across different consult packages, but you can set this variable explicitly for consult-mu.
288
289The recommended option is to set this to ='any= so as you naviagte over the candidates you see previews updated.
290
291#+begin_src emacs-lisp
292(setq consult-mu-preview-key 'any)
293#+end_src
294
295If the option above slows down your system, and you only want to load previews on demand, then you can set it to a specific key such as ="M-o"=.
296
297#+begin_src emacs-lisp
298(setq consult-mu-preview-key "M-o")
299#+end_src
300
301You can also turn previews off by setting this variable to =nil=, but this is not generally recommended.
302
303
304***** =consult-mu-highlight-matches=
305This variable determines if consult-mu highlights search queries in minibuffer or preview buffers. By default it is set to t.
306
307When it is set to t, all matches of the search term are highlighted in the minibuffer allowing you to notice why the email is a search hit.
308
309For example in the screenshot below, I am searching for the term =consult= and all the matches of consult are highlighted in the titles.
310#+attr_org: :width 800px :height nilpx
311#+attr_latex: :width 800px :height nilpx
312#+attr_html: :width 800px :height nilpx
313[[https://github.com/armindarvish/consult-mu/blob/screenshots/screenshots/consult-mu-highlight-matches-minibuffer.png]]
314
315
316Furthermore if I look at a preview, all the instances of consult-gh matches in the preview buffer are also highlighted:
317#+attr_org: :width 800px :height nilpx
318#+attr_latex: :width 800px :height nilpx
319#+attr_html: :width 800px :height nilpx
320[[https://github.com/armindarvish/consult-mu/blob/screenshots/screenshots/consult-mu-highlight-matches-preview.png]]
321
322Note that when the candidate is selected (i.e. by pressing =RET=), the highlight overlay is turned off, so you can see the orignial message as is, but you can call =consult-mu-overlays-toggle= (i.e. =M-x consult-mu-overlays-toggle=) to see the highlights of the query again.
323#+attr_org: :width 800px :height nilpx
324#+attr_latex: :width 800px :height nilpx
325#+attr_html: :width 800px :height nilpx
326[[https://github.com/armindarvish/consult-mu/blob/screenshots/screenshots/consult-mu-highlight-matches-view.png]]
327
328***** =consult-mu-action=
329This variable stores the function that is called when a message is selected (i.e. =RET= is pressed in the minibuffer). By default it is bound to =consult-mu--view-action= which opens both the headers buffer and view buffer and shows the content of the selected message.
330
331**** compose
332These variables are only available after loading the compose module;
333#+begin_src emacs-lisp
334(require 'consult-mu-compose)
335#+end_src
336
337***** =consult-mu-compose-use-dired-attachment=
338This variable defines, whether =consult-mu= uses dired buffers for selecting files to attach. If it is set to ='always=, consult-mu will always jump to a dired buffer for selecting files to attach. It it is set to ='in-dired=, consult-mu only uses dired (a.k.a. file at point or marked files) for attaching files when already inside a dired buffer. If it is set to =nil=, consult-mu uses minibuffer completion for file selection for attachments.
339***** =consult-mu-large-file-warning-threshold=
340A threshold to make sure very large files are not accidentally opened when previewing files that the user wants to attach to an email. If file is larger than this threshold, the user is asked to confirm before loading the file buffer.
341
342***** =consult-mu-compose-preview-key=
343This is similar to =consult-mu-preview-key= but only for =consult-mu-compose=. This is used to preview files when selecting files for attachments. By default, it is set to the follow the value of =consult-mu-preview-key= to keep consistent experience across the package. *But it is recommended to change this to something other than ='any= becuase otherweise every file is previewd as the user is navigating through folders to select files to attach to an email.
344
345#+begin_src emacs-lisp
346(setq consult-mu-compose-preview-key "M-o")
347#+end_src
348
349***** =consult-mu-embark-attach-file-key=
350This variable defines a key that can be bound to =consult-mu-compose-attach= in =emabrk-file-map=, therefore one can call embark on any file and use this key to attach it to an email. This can be bound to "a" or "C-a":
351
352#+begin_src emacs-lisp
353(setq consult-mu-embark-attach-file-key "C-a")
354#+end_src
355
356Running the function =consult-mu-compose-embark-bind-attach-file-key= binds this key, but any other key can be passed to this function as well to override the customization variable:
357
358#+begin_src emacs-lisp
359(consult-mu-compose-embark-bind-attach-file-key)
360#+end_src
361
362**** contacts
363These variables are only available after loading the contacts module;
364#+begin_src emacs-lisp
365(require 'consult-mu-contacts)
366#+end_src
367
368***** =consult-mu-contacts-group-by=
369This variable determines what field is used to group contacts, which allows quick movement between groups (For example with =vertico-next-group= if you use [[https://github.com/minad/vertico][vertico]])
370
371By default it is set to name. But can be any of the following keywords:
372
373 - =:name= group by contact name
374 - =:email= group by email of the contact
375 - =:domain= group by the domain of the contact's email (e.g. domain.com in user@domain.com)
376 - =:user= group by the ncontact's user name (e.g. user in user@domain.com)
377
378For example if =consult-mu-contacts-group-by= is set to =:domain= then the domain of the email address is used to group contacts. This is useful for example if you are looking for all emails from a specific company!
379
380***** =consult-mu-contacts-action=
381This variable stores the function that is called when a contact is selected (i.e. =RET= is pressed in the minibuffer). By default it is bound to =consult-mu-contacts--list-messages-action= which searches for all the messages from that contact by calling =consult-mu=. You can also set this action to other functions such =consult-mu-contacts--insert-email-action= or =consult-mu-contacts--copy-email-action= for inserting or copying the email from contact.
382
383Note that the default action for =consult-mu-contacts-embark= is inserting the email. This is useful for quickly adding contacts when composing messages. You cna also use =embark-collect= or =embark-export= to select multiple contacts and act on all of them (e.g. insert all in "To:" field in a compose buffer).
384
385***** =consult-mu-contacts-ignore-list=
386 This is a list of rgexp that gets ignored when searching contacts.
387 This is useful to filter certain invalid addreses (e.g. "no-reply" addresses from contacts). For example you can remove no-reply adresses by setting this variable as follows;
388 #+begin_src emacs-lisp
389(setq consult-mu-contacts-ignore-list '("^.*no.*reply.*$"))
390
391 #+end_src
392
393***** =consult-mu-contacts-ignore-case-fold-search=
394This variable defines whether =consult-mu-contacts= uses case insensitive search when matching against =consult-mu-contacts-ignore-list= (see above). if you set this to =t=, it will preform case *in*sensitive match.
395
396* Features and Demos
397For a detailed description of why this package is useful, and some useful screenshosts showing work flows, you can read my blog post here:
398
399[[https://www.armindarvish.com/post/improve_your_mu4e_workflow_with_consult-mu/]]
400
401** Search
402consult-mu uses [[https://github.com/minad/consult#asynchronous-search][consult's asynchronous search]] feature. In order to search a query you can type your standard Mu4e query after =#= sign in the minibuffer. Here are some examples:
403
404#+begin_example
405#tickets
406#flag:unread
407#(flag:unread AND maildir:/inbox)
408#(maildir:/drafts OR maildir:/sent)
409#+end_example
410
411
412In addition, you can pass command line arguments that you can normally pass to =mu= (see =mu find --help=) in the command line to consult-mu by using a =--= separator like this:
413
414#+begin_example
415#tickets -- --maxnum 500 -s s
416#+end_example
417
418In the example above, the maximum number of results to retrieve are set to 500 (=--maxnum 500=) and the result are sorted by the subject (=-s s=). For more details on command line options, you can refer to mu's help (i.e. run =mu find --help= in terminal).
419
420Once consult-mu retrieves a list of results, you can further narrow down the list of candidates by using a second =#=.
421
422#+begin_example
423#tickets -- -z #concert
424#+end_example
425
426In the example above, we first run a search for *tickets* and sort the results with reverse order (using =-z= option) then narrow down the list of candidates by the word, *concert*.
427
428Note that narrow downs are similar to other narrow downs in the minibuffer, therefore here you cannot use mu4e search syntax but you can use regular expressions. Also, the narrow down searches in the entire header string, therefore you can use any information available in the headers (date, title, email addresses, flags,...) for narrow down. (to change the format of the header, take a look at [[id:155CF359-63D0-4D4D-B0BD-7C089E2D9B3C][=consult-mu-headers-template=]]. Here is a screenshot for doing a search and narrow:
429
430#+ATTR_ORG: :width 800px
431#+ATTR_LATEX: :width 800px
432#+ATTR_HTML: :width 800px
433[[https://github.com/armindarvish/consult-mu/blob/screenshots/screenshots/consult-mu-search-narrow.gif]]
434
435** Saved Searches and Quick Access History
436consult-mu does have a history variable and keeps record of your previous searches. These searches can quickly be accessed using =previous-history-element= (bound to =M-p= by default). In addition, you can have a list of saved searches for quick access by modifying the variable =consult-mu-saved-searches-dynamic= or =consult-mu-saved-searches-async= for =consult-mu-dynamic= and =consult-mu-async=, respectively. These are lists of mu4e query strings that get added to future-history when you run consult-mu-dynamic or consult-mu-async. By default, =consult-mu-saved-searches-async= is set to inherit from =consult-mu-saved-searches-dynamic=, but you can modify them independently. This allows separating saved searches that are more suitable for async search from those that are better done with dynamic collection.
437
438Similarly =consult-mu-compose-attach=, =consult-mu-compose-detach=, and =consult-mu-contacts= have their own history elements as wels future history elemetns (for example email in the current message buffer gets added to future-history for searching contacts).
439
440** Integration with Embark
441consult-mu provides integration with [[https://github.com/oantolin/embark][embark]] defined in =consult-mu-embark.el= If you use embark, you can use these commands to run actions on the search results directly from minibuffer. For example, you can call =consult-mu-embark-reply= to reply to a message or =consult-mu-embark-mark-for-refile= to refile (a.k.a. archive) the message and so on.
442
443For marking, note that, by default when consult-mu-embark is loaded, it creates a function for every item in the =mu4e-marks= and binds them to the =:char= for that mark in =consult-mu-embark-messages-actions-map=.
444
445You can also use =embark-select= and =embark-act-all= to run the same command on multiple messages, but this only works in consult-mu-dynamic and not so well with consult-mu-async because with consult-my-async, only one message at a time is added to the headers view and therefore running commands on multiple candidates with embark is not supported.
446
447** Extras (attachments, contacts, ...)
448In addition to improvements for searching, consult-mu also provides extra features for composing, contacts, ... These features are defined in the files under the =extras= folder.
449*** Attachments
450By default, attaching files to emails with mu4e is not very intuitive or interactive. If you use [[https://github.com/jeremy-compostella/org-msg][org-msg]], it provides an interactive command for attaching files, but it is only for one file at a time and for each file you need to go through some menus. Some emacs configs, like [[https://github.com/doomemacs/doomemacs][doomemacs]], do provide their own commands to allow using a dired buffer to attach multiple files to an email (see [[https://github.com/doomemacs/doomemacs/blob/986398504d09e585c7d1a8d73a6394024fe6f164/modules/email/mu4e/autoload/email.el#L272C8-L272C26][+mu4e/attach-files]]). Personally, I prefer using a minibuffer completion (instead of switching to a dired buffer) unless I am already in a dired buffer. The [[file:extras/consult-mu-compose.el][consult-mu-compose]] provides the utilities and customization settings to achieve interactive multi-file attachment.
451
452To use attachment extras, you need to load the compose module by:
453#+begin_src emacs-lisp
454(require 'consult-mu-compose)
455(require 'consult-mu-compose-embark) ;;optionally load embark actions for compose
456#+end_src
457
458It is highly recommended that you also change the preview key binding for =consult-mu-compose= to something other than ='any= because seeing a preview of every file when you are navigating through the minibuffer may be slow and annoying. It's easier to get a preview of the files you are interested in with a key binding like =M-o= than to see a preview of every file.
459
460#+begin_src emacs-lisp
461(setq consult-mu-compose-preview-key "M-o")
462#+end_src
463**** Attaching Files
464=consult-mu-compose-attach= provides an interactive command to attach files to an email and it tries its best to provide an intuitive interface.
465
466Here are some features:
467
468- interactively selecitng draft compose buffer:
469In a compose buffer (e.g. =mu4e-compose-mode=, =message-mode=, =org-msg-edit-mode=), it assumes that the user wants to attach files to the current message. In other buffers it asks the user to select a current compose buffer or creates a new one if noe exists.
470
471- interactively selecting files:
472For selecting files, it can either use minibuffer completion or a dired buffer. If the customization variable =consult-mu-compose-use-dired-attachment= is set to =always=, it always uses dired buffer similar to what dom emacs does. If it is set to ='in-dired= it only uses dired when inside a dired-mode buffer. and if it is set to =nil=, it will always use minibuffer completion.
473When using dired to chose files, one can mark multiple files to attach to a message. Furthermore, if the command =consult-mu-compose-attach= is called from within a compose buffer (e.g. =mu4e-compose-mode=, =message-mode=, =org-msg-edit-mode=), runing =embrak-dwim= on the file will attach the file to the current message. This is specially useful if you use =embark-dwim= with =no-quit= option ([[https://github.com/oantolin/embark#quitting-the-minibuffer-after-an-action][embark#quitting-the-minibuffer-after-an-action]]). Here is an example on how to define =embark-dwim-noquit=:
474
475#+begin_src emacs-lisp
476(defun embark-dwim-noquit ()
477 "Run action but don't quit the minibuffer afterwards."
478 (interactive)
479 (let ((embark-quit-after-action nil))
480 (embark-dwim)))
481#+end_src
482Then you can just attach multiple files to the same message from any folder by using the minibuffer completion. See the screenshot in my blog post here: https://www.armindarvish.com/post/improve_your_mu4e_workflow_with_consult-mu/
483
484**** Removing attachments:
485Similarly, =consult-mu-compose-detach= provides an interactive command to remove files that are already attached to the current message. It uses minibuffer completion to select a file to remove. Similar to what mentioned above, embark-dwim (or a no-quit version of it) can be used to remove multiple files from the same message. note that the minibuffer completion list does not get dynamically updated when a file is removed (This is something I may improve in the future but for now it works just fine I think).
486See the screenshot in my blog post here: https://www.armindarvish.com/post/improve_your_mu4e_workflow_with_consult-mu/
487
488
489*** Contacts
490=consult-mu-contacts= provides an interactive way to search mu contacs. By default, when a candidate is selected, all the emails form the contact is searched by using =consult-mu=. This can be customized to oyher functions (e.g. compose an email to the contact) by seeting =consult-mu-contacts-action=. Note that mu does not have a stored list of contacts. Rather, contacts are generated dynamically from the email fields in all messages in the database, including email fields that might be invalid! To use =consult-mu-contacts=, you need to load the contacts module by:
491
492#+begin_src emacs-lisp
493(require 'consult-mu-contacts)
494(require 'consult-mu-contacts-embark) ;;optionally load embark actions for contacts
495#+end_src
496
497See the screenshot in my blog post here: https://www.armindarvish.com/post/improve_your_mu4e_workflow_with_consult-mu/
498
499
500* Bug reports
501To report bug, first check if it is already reported in the [[https://github.com/armindarvish/consult-mu/issues][*issue tracker*]] and see if there is an existing solution or add relevant comments and discussion under the same issue. If not file a new issue following these steps:
502
5031. Make sure the dependencies are installed, and both =mu4e= and =consult= work as expected.
504
5053. Remove the package and install the latest version (along with dependencies) and see if the issue persists.
506
5074. In a bare bone vanilla Emacs (>=28) (e.g. =emacs -Q=), install the latest version of consult-mu (and its dependencies) without any configuration or other packages and see if the issue still persists.
508
5095. File an issue and provide important information and context in as much detail as possible in your bug report. Important information can include:
510- Your operating system, version of Emacs (or the version of emacsen you are using), version of mu/mu4e and consult (see [[https://github.com/emacsorphanage/pkg-info][pkg-info]]).
511- The installation method and the configuration you are using with your consult-mu.
512- If there is an error message, turn debug-on-error on (by =M-x toggle-debug-on-error=) and include the backtrace content in your report.
513- If the error only exists when you have some other packages installed, list those packages (e.g. problem happens when evil is installed)
514- It would be useful, if you can look at consult-mu buffers while the minibuffer command is active (by default they are named =*consult-mu-headers*= and =*consult-mu-view*= buffers) and report whether theya re getting populated properly or not.
515
516* Contributions
517This is an open source package, and I appreciate feedback, suggestions, ideas, etc. There are lots of functionalities that can be added to this package to improve different user's workflows, so if you have some ideas, feel free to file an issue for a feature request.
518
519If you want to contribute to the code, please note that the main branch is currently stable (as stable as a work in progress like this can be) and the develop branch is the current work in progress. So, *start from the develop branch* to get the latest work-in-progress updates and create a new branch with names such as feature/name-of-the-feature or fix/issue, ... Do the edits and then create a new pull request to merge back with the *develop* branch when you are done with your edits.
520
521Importantly, keep in mind that I am using a *literate programming approach* (given that this is a small project with very limited number of files) where everything goes into *consult-mu.org* and then gets tangled to appropriate files (for now that includes consult-mu.el and consult-mu-embark.el). If you open a pull-request where you directly edited the .el files, I will likely not approve it because that will then get overwritten later when I tangle from the .org file. In other words, *Do Not Edit The .el Files!* only edit the .org file and tangle to .el files.
522
523
524* What about other packages? Why we need a new package such as consult-mu?
525While mu4e's built-in search is great and provides ways to edit search terms (e.g. =mu4e-search-edit=) or toggle search properties (e.g. =mu4e-search-toggle-property=), the interface is not really intuitive. We are simply too impatient in 2024 to use a static search field and edit in steps. A dynamically updated search results is somewhat expected with any modern tools. Try searching in Thunderbird, or Outlook, and you instantly see suggestions and results. This is what consult-mu provides. Hopefully, in the future a dynamics search approach comes built-in with mu4e but until then, this package intends to fill the gap.
526
527*** What about alternative approaches like [[https://github.com/seanfarley/counsel-mu][counsel-mu]] or [[https://github.com/emacsmirror/consult-notmuch][consult-notmuch]]?
528
529You can read my blog post for some thoughts on this:
530 https://www.armindarvish.com/post/improve_your_mu4e_workflow_with_consult-mu/
531
532
533Both [[https://github.com/seanfarley/counsel-mu][counsel-mu]] and [[https://github.com/emacsmirror/consult-notmuch][consult-notmuch]] inspired this package. But this package provides soemthing more than those packages. Here is the comparison:
534
535- [[https://github.com/seanfarley/counsel-mu][counsel-mu]]: counsel-mu provides an async search for mu, and when the candidate is selected, the single message is loaded by using =mu4e-view-message-with-message-id=. =consult-mu= takes a similar approach in =consult-mu-async= but expands on that with the following features:
536
537 a. ability to dynamically add command line options (e.g. using =query -- -s s -z= as input).
538 This includes dynamically changing the number of results. This is important because with counsel-mu, one ha to change the variable =mu4e-search-results-limit= globally to see many results, which then affects every mu4e-search that is done.
539
540 b. ability to load a preview without leaving minibuffer search
541
542 c. ability to use emabrk actions on candidates from the minibuffer with =consult-mu-embark=
543
544 d. ability to see whole threads when selecting a candidate rather than just single messages
545
546 In addition, =consult-mu= provides the =consult-mu-dynamic= interactive command that uses dynamic collection using =mu4e-search= (and not =mu= commands). This allows doing dynamic search in the minibuffer and then getting a full list of results similar to the built-in mu4e-search.
547
548
549- [[https://github.com/emacsmirror/consult-notmuch][consult-notmuch]]: consult-notmuch is great IF you use [[https://notmuchmail.org/notmuch-emacs/][notmuch-emacs]]. consult-mu on the other hand provides similar functionality for [[https://github.com/djcb/mu][mu/mu4e]].
550 The comparison therefore comes down to comparing mu4e and notmuch. While notmuch is light and fast, I think it lacks lots of basic functionalities of an email client. It is supposed to be *not much* after all! As a result, using notmuch as an everyday email client can be challenging especially if you want to use it along with other IMAP-based email clients (e.g. mobile apps). This is because the philosophy of using tags instead of folders requires a complete redesign of some workflows. While some services, like Gmail, use labels, others may not and even if they do, in IMAP-based clients labels are treated as different folders and therefore syncing back custom notmuch labels everywhere (e.g. between notmuch on your desktop machine and your mobile app client) becomes tricky. Of course, in Emacs nothing is impossible and there are ways to improve the experience by adding additional custom elisp (see [[https://www.youtube.com/watch?v=g7iF11qamh8][Emacs: Notmuch demo (notmuch.el) - YouTube]] and [[https://www.reddit.com/r/emacs/comments/qo3eza/notmuch_as_an_alternative_to_mu4e/?share_id=8WUviRO3gGGIO4bQgoSzU&utm_content=2&utm_medium=android_app&utm_name=androidcss&utm_source=share&utm_term=10][Notmuch as an alternative to mu4e : emacs]] for some examples), but at the end, the results will still likely lack some important features (like multi-account contexts, ...).
551
552More importantly, a common reason to choose notmuch over mu4e is its speed, but using the underlying =mu= server and command line can also be very fast. In fact, at least in my own tests, =consult-mu-async=, which uses the command line =mu= commands was faster than =consult-notmuch=. With the latest release of mu4e (version 1.12), even =consult-mu-dynamics= (a.k.a. built-in mu4e-search) is now very fast. Therefore, using =mu4e= with =consult-mu= provides the best of both worlds to me. When I need speed and simplicity, I can use =consult-mu-async= to do a fast search (and find thousands of hits) and quickly narrow down to what I am looking for; and when I need a more complete and full-feature client I can use =mu4e= built-in functionalities, and with addition of dynamically built searches with =consult-mu-dynamic=, I have a modern intuitive interface as well.
553
554* Acknowledgments
555Obviously this package would not have been possible without the fabulous [[https://github.com/djcb/mu][mu/mu4e]], and [[https://github.com/minad/consult][consult]] packages. It also took inspiration from other packages including but not limited to [[https://github.com/seanfarley/counsel-mu][counsel-mu]], [[https://github.com/emacsmirror/consult-notmuch][consult-notmuch]], and [[https://github.com/doomemacs/doomemacs][doomemacs]].