[Emacs] Mu4e documentation, litterate programming ftw

This commit is contained in:
Lucien Cartier-Tilet 2021-06-11 16:28:25 +02:00
parent 39d9f0e74b
commit ac01ba4eaf
Signed by: phundrak
GPG Key ID: BD7789E705CB8DCA

View File

@ -576,6 +576,27 @@ prefix them with a comma (Ive taken this habit from Spacemacs).
(general-auto-unbind-keys))
#+end_src
#+name: general-keybindings-gen
#+header: :tangle no :exports none :results value
#+begin_src emacs-lisp :var table=mu4e-keybindings-view-tbl
(mapconcat (lambda (line)
(let ((key (car line))
(function (cadr line))
(comment (caddr line)))
(format "\"%s\" %s" key
(if (string= "" comment)
(if (string= "nil" function)
"nil"
(concat "#'" function))
(format "'(%s :wk \"%s\")"
comment
(if (string= "" function)
"nil"
comment))))))
table
"\n")
#+end_src
** Evil
#+begin_src emacs-lisp
(use-package evil
@ -729,12 +750,28 @@ grow the ~mu4e-headers~ buffer when viewing an email.
#+end_src
*** Elfeed
*** Email - Mu4e
*** Email
**** Basic configuration
As seen below, I use ~org-msg~ to compose my emails, which includes by
default my signature. Therefore, there is no need for Emacs itself to
know about it since I dont want it to include it a second time after
~org-msg~ already did.
#+begin_src emacs-lisp
(setq message-signature nil
mail-signature nil)
#+end_src
**** Mu4e
Mu4e is a very eye-pleasing email client for Emacs, built around ~mu~
and which works very well with ~mbsync~ (found in Archs ~isync~ package).
For me, the main advantage of mu4e is it has a modern interface for
emailing, and quite straightforward. I tried a couple of other email
clients for Emacs, and I even was for some time a Gnus user, but in
the end, mu4e really works best for me. Below youll find my
configuration for the ~mu4e~ package itself.
Quick sidenote: on ArchLinux, youll need to install either ~mu~ or
~mu-git~ from the AUR in order to use mu4e.
#+begin_src emacs-lisp
(use-package mu4e
:after all-the-icons
@ -754,7 +791,11 @@ grow the ~mu4e-headers~ buffer when viewing an email.
:config
(progn
<<mu4e-keybindings>>
<<mu4e-keybindings-view>>
<<mu4e-keybindings-header>>
<<mu4e-keybindings-header-no-leader>>
<<mu4e-keybindings-message>>
(setq mu4e-compose-signature nil)
(when (fboundp 'imagemagick-register-types)
@ -777,30 +818,22 @@ grow the ~mu4e-headers~ buffer when viewing an email.
mu4e-refile-folder "/Archive"
mu4e-sent-folder "/Sent"
mu4e-drafts-folder "/Drafts"
mu4e-change-filenames-when-moving t
mu4e-update-interval 60
mu4e-compose-format-flowed t
mu4e-view-show-addresses t
mu4e-sent-messages-behaviour 'sent
mu4e-hide-index-messages t
;; try to show images
mu4e-view-show-images t
mu4e-view-show-images t ; try to show images
mu4e-view-image-max-width 600
;; configuration for sending mail
message-send-mail-function #'smtpmail-send-it
message-send-mail-function #'smtpmail-send-it ; how to send an email
smtpmail-stream-type 'starttls
message-kill-buffer-on-exit t ; close after sending
;; start with the first (default) context
mu4e-context-policy 'pick-first
;; compose with the current context, or ask
mu4e-compose-context-policy 'ask-if-none
;; use ivy
mu4e-completing-read-function #'ivy-completing-read
;; no need to ask
mu4e-confirm-quit t
mu4e-header-fields
'((:account . 12)
mu4e-context-policy 'pick-first ; start with first (default) context
mu4e-compose-context-policy 'ask-if-none ; compose with current context, or ask
mu4e-completing-read-function #'ivy-completing-read ; use ivy
mu4e-confirm-quit t ; no need to ask
mu4e-header-fields '((:account . 12)
(:human-date . 12)
(:flags . 4)
(:from . 25)
@ -810,61 +843,19 @@ grow the ~mu4e-headers~ buffer when viewing an email.
(setq mail-user-agent 'mu4e-user-agent)
;; Use fancy icons
(setq mu4e-use-fancy-chars t
mu4e-headers-draft-mark `("D" . ,(all-the-icons-faicon "pencil":height 0.8))
mu4e-headers-flagged-mark `("F" . ,(all-the-icons-faicon "flag":height 0.8))
mu4e-headers-new-mark `("N" . ,(all-the-icons-faicon "rss":height 0.8))
mu4e-headers-passed-mark `("P" . ,(all-the-icons-faicon "check":height 0.8))
mu4e-headers-replied-mark `("R" . ,(all-the-icons-faicon "reply":height 0.8))
mu4e-headers-seen-mark `("S" . ,(all-the-icons-faicon "eye":height 0.8))
mu4e-headers-unread-mark `("u" . ,(all-the-icons-faicon "eye-slash":height 0.8))
mu4e-headers-trashed-mark `("T" . ,(all-the-icons-faicon "trash":height 0.8))
mu4e-headers-attach-mark `("a" . ,(all-the-icons-faicon "paperclip":height 0.8))
mu4e-headers-encrypted-mark `("x" . ,(all-the-icons-faicon "lock":height 0.8))
mu4e-headers-signed-mark `("s" . ,(all-the-icons-faicon "certificate":height 0.8)))
<<mu4e-fancy-marks>>
;; Set bookmarks
<<mu4e-bookmarks>>
(setq mu4e-bookmarks
`((,(s-join " "
'("NOT flag:trashed"
"AND (maildir:/Inbox OR maildir:/Junk)"
"AND NOT to:CONLANG@LISTSERV.BROWN.EDU"
"AND NOT to:AUXLANG@LISTSERV.BROWN.EDU"
"AND NOT to:ateliers-emacs@framalistes.org"
"AND NOT to:ateliers-paris@emacs-doctor.com"
"AND NOT list:ateliers-emacs.framalistes.org"
"AND NOT list:ateliers-paris.emacs-doctor.com"))
"Inbox" ?i) ;; Inbox without the linguistics mailing lists
(,(s-join " "
'("NOT flag:trashed"
"AND (maildir:/Inbox OR maildir:/Junk)"
"AND (f:/.*up8\.edu|.*univ-paris8.*/"
"OR c:/.*up8\.edu|.*univ-paris8.*/"
"OR t:/.*up8\.edu|.*univ-paris8.*/)"))
"University" ?u) ;; University-related emails
(,(s-join " "
'("to:CONLANG@LISTSERV.BROWN.EDU"
"OR to:AUXLANG@LISTSERV.BROWN.EDU"))
"Linguistics" ?l) ;; linguistics mailing lists
(,(s-join " "
'("list:ateliers-emacs.framalistes.org"
"OR to:ateliers-paris@emacs-doctor.com"
"OR list:ateliers-paris.emacs-doctor.com"))
"Emacs" ?e) ;; Emacs mailing list
("maildir:/Sent" "Sent messages" ?s)
("flag:unread AND NOT flag:trashed" "Unread messages" ?U)
("date:today..now AND NOT flag:trashed" "Today's messages" ?t)
("date:7d..now AND NOT flag:trashed" "Last 7 days" ?w)
("date:1m..now AND NOT flag:trashed" "Last month" ?m)
("date:1y..now AND NOT flag:trashed" "Last year" ?y)
("flag:trashed AND NOT flag:trashed" "Trash" ?T)
("mime:image/* AND NOT flag:trashed" "Messages with images" ?p)))
;; mu4e-headers-mode config
<<mu4e-headers-mode>>
;; Add a column to display what email account the email belongs to.
(add-to-list 'mu4e-header-info-custom
'(:account
:name "Account"
:shortname "Account"
:help "Which account this email belongs to"
:name "Phundrak Main"
:shortname "Phundrak"
:help "Main email of phundrak"
:function
(lambda (msg)
(let ((maildir (mu4e-message-field msg :maildir)))
@ -888,23 +879,237 @@ grow the ~mu4e-headers~ buffer when viewing an email.
(add-to-list 'mu4e-view-actions '("PDF view" . mu4e-action-open-as-pdf) t)))
#+end_src
#+name: mu4e-keybindings
***** Bookmarks
In mu4e, the main focus isnt really mail directories such as your
inbox, your sent messages and such, but instead you manipulate
bookmarks which will show you emails depending on tags. This mean you
can create some pretty customized bookmarks that go way beyound your
simple inbox, outbox and all. Actually, four of my bookmarks have a
couple of filtering:
- anything in my inbox linked to my university
- the [[https://emacs-doctor.com/lists/listinfo][emacs-doctor mailing list]] (French Emacs mailing list)
- the conlang mailing lists
- and my inbox for any mail not caught by any of these filters
And all of them will have the requirement not to display any trashed
email. Actually, all of my bookmarks will have this requirement,
except for the bookmark dedicated to them as well as my sent emails.
Ill add these latter requirements later.
Here are the requirements for my university bookmark. The regex
matches any email address which contains either ~up8.edu~ or
~univ-paris8~, which can be found in email addresses from the University
Paris 8 (my university).
#+name: mu4e-bookmarks-filter-uni
#+headers: :tangle no :exports both :cache yes
#+begin_src emacs-lisp
(string-join '("f:/.*up8\.edu|.*univ-paris8.*/"
"c:/.*up8\.edu|.*univ-paris8.*/"
"t:/.*up8\.edu|.*univ-paris8.*/")
" OR ")
#+end_src
#+RESULTS[55041e7ce5b7c7b228c9fd6e1c9715f677094c8e]: mu4e-bookmarks-filter-uni
: f:/.*up8.edu|.*univ-paris8.*/ OR c:/.*up8.edu|.*univ-paris8.*/ OR t:/.*up8.edu|.*univ-paris8.*/
As for the Emacs-doctor list, I need to match both the current, modern
mailing list address but also its old address.
#+name: mu4e-bookmarks-filter-emacs-list
#+headers: :tangle no :exports both :cache yes
#+begin_src emacs-lisp
(mapconcat (lambda (address)
(mapconcat (lambda (flag)
(concat flag ":" address))
'("list" "to" "from")
" OR "))
'("ateliers-emacs.framalistes.org" "ateliers-paris.emacs-doctor.com")
" OR ")
#+end_src
#+RESULTS[463132dbde653749ac07ee8e1263733ee15b5847]: mu4e-bookmarks-filter-emacs-list
: list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com
When it comes to the conlang mailing list, lets not match anything
from or to them. Ill also include the auxlang mailing list Im not
subscribed anymore, but itll keep my inbox clean.
#+name: mu4e-bookmarks-filter-conlang-list
#+headers: :tangle no :exports both :cache yes
#+begin_src emacs-lisp
(mapconcat (lambda (address)
(mapconcat (lambda (flag)
(concat flag ":" address))
'("from" "to" "list")
" OR "))
'("CONLANG@LISTSERV.BROWN.EDU" "AUXLANG@LISTSERV.BROWN.EDU")
" OR ")
#+end_src
#+RESULTS[5565a39c69d99277cffbf4e4be88211ab463543b]: mu4e-bookmarks-filter-conlang-list
: from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU
As I said earlier, something that will often come back in my bookmarks
is the emails must not be trashed to appear. I want also to display
junk emails, so I end up with the following rule:
#+name: mu4e-bookmarks-default-filter
#+headers: :tangle no :exports both :cache yes
#+begin_src emacs-lisp
(string-join `("NOT flag:trashed"
,(format "(%s)" (mapconcat (lambda (maildir) (concat "maildir:" maildir))
'("/Inbox" "/Junk")
" OR ")))
" AND ")
#+end_src
#+RESULTS[88f8a5401e240f98fd64fe227699f5ddfe6d5730]: mu4e-bookmarks-default-filter
: NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk)
And for the last string-generating code, lets describe my main inbox:
#+name: mu4e-bookmarks-inbox-filters
#+headers: :exports both :tangle no :cache yes
#+begin_src emacs-lisp
(string-join (cons
<<mu4e-bookmarks-default-filter>>
`(
,(format "(%s)"
<<mu4e-bookmarks-filter-conlang-list>>)
,(format "(%s)"
<<mu4e-bookmarks-filter-emacs-list>>)
,(format "(%s)"
<<mu4e-bookmarks-filter-uni>>)))
" AND NOT ")
#+end_src
#+RESULTS[96218e66b6554a2b8b1f09f016d9af1d238bb79b]: mu4e-bookmarks-inbox-filters
: NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu|.*univ-paris8.*/ OR c:/.*up8.edu|.*univ-paris8.*/ OR t:/.*up8.edu|.*univ-paris8.*/)
We can finally define our bookmarks! The code reads as follows:
#+name: mu4e-bookmarks
#+begin_src emacs-lisp :tangle no :cache yes
(setq mu4e-bookmarks
`((:name "Inbox"
:key ?i
:query ,(format "%s"
<<mu4e-bookmarks-inbox-filters>>))
(:name "Linguistics"
:key ?l
:query ,(format "%s AND %s"
<<mu4e-bookmarks-inbox-filters>>
<<mu4e-bookmarks-filter-conlang-list>>))
(:name "Emacs"
:key ?e
:query ,(format "%s AND %s"
<<mu4e-bookmarks-inbox-filters>>
<<mu4e-bookmarks-filter-emacs-list>>))
(:name "University"
:key ?u
:query ,(format "%s AND %s"
<<mu4e-bookmarks-inbox-filters>>
<<mu4e-bookmarks-filter-uni>>))
(:name "eshell-info-banner" :key ?E :query ,(format "%s AND %s"
<<mu4e-bookmarks-inbox-filters>>
"list:eshell-info-banner.el.Phundrak.github.com"))
(:name "Sent" :key ?s :query "maildir:/Sent")
(:name "All Unread" :key ?U :query "flag:unread AND NOT flag:trashed")
(:name "Today" :key ?t :query "date:today..now AND NOT flag:trashed")
(:name "This Week" :key ?w :query "date:7d..now AND NOT flag:trashed")
(:name "This Month" :key ?m :query "date:1m..now AND NOT flag:trashed")
(:name "This Year" :key ?y :query "date:1y..now AND NOT flag:trashed")))
#+end_src
#+RESULTS[3e4e7f607d9f961a27594ddb98037ec25b2c94f7]: mu4e-bookmarks
| :name | Inbox | :key | 105 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) | | | |
| :name | Linguistics | :key | 108 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU | | | |
| :name | Emacs | :key | 101 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com | | | |
| :name | University | :key | 117 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/ |
| :name | eshell-info-banner | :key | 69 | :query | NOT flag:trashed AND (maildir:/Inbox OR maildir:/Junk) AND NOT (from:CONLANG@LISTSERV.BROWN.EDU OR to:CONLANG@LISTSERV.BROWN.EDU OR list:CONLANG@LISTSERV.BROWN.EDU OR from:AUXLANG@LISTSERV.BROWN.EDU OR to:AUXLANG@LISTSERV.BROWN.EDU OR list:AUXLANG@LISTSERV.BROWN.EDU) AND NOT (list:ateliers-emacs.framalistes.org OR to:ateliers-emacs.framalistes.org OR from:ateliers-emacs.framalistes.org OR list:ateliers-paris.emacs-doctor.com OR to:ateliers-paris.emacs-doctor.com OR from:ateliers-paris.emacs-doctor.com) AND NOT (f:/.*up8.edu | .*univ-paris8.*/ OR c:/.*up8.edu | .*univ-paris8.*/ OR t:/.*up8.edu | .*univ-paris8.*/) AND list:eshell-info-banner.el.Phundrak.github.com | | | |
| :name | Sent | :key | 115 | :query | maildir:/Sent | | | | | | |
| :name | All Unread | :key | 85 | :query | flag:unread AND NOT flag:trashed | | | | | | |
| :name | Today | :key | 116 | :query | date:today..now AND NOT flag:trashed | | | | | | |
| :name | This Week | :key | 119 | :query | date:7d..now AND NOT flag:trashed | | | | | | |
| :name | This Month | :key | 109 | :query | date:1m..now AND NOT flag:trashed | | | | | | |
| :name | This Year | :key | 121 | :query | date:1y..now AND NOT flag:trashed | | | | | | |
***** Getting Fancy
Im not a huge fan of mu4es default icons marking my emails, so Ill
redefine them as follows. Be aware the name of these icons are from
/faicon/ in the package ~all-the-icons~.
#+name: mu4e-fancy-marks-tbl
| Mark | Flag | Icon |
|-----------+------+-------------|
| draft | D | pencil |
| flagged | F | flag |
| new | N | rss |
| passed | P | check |
| replied | R | reply |
| seen | S | eye |
| unread | u | eye-slash |
| trashed | T | trash |
| attach | a | paperclip |
| encrypted | x | lock |
| signed | s | certificate |
#+name: mu4e-fancy-marks-gen
#+header: :tangle no :exports none :results value
#+begin_src emacs-lisp :var table=mu4e-fancy-marks-tbl
(mapconcat (lambda (line)
(let ((mark (car line))
(flag (cadr line))
(icon (caddr line)))
(format "mu4e-headers-%s-mark `(\"%s\" . ,(all-the-icons-faicon \"%s\" :height 0.8))"
mark
flag
icon)))
table
"\n")
#+end_src
#+RESULTS:
#+begin_example
mu4e-headers-draft-mark `("D" . ,(all-the-icons-faicon "pencil" :height 0.8))
mu4e-headers-flagged-mark `("F" . ,(all-the-icons-faicon "flag" :height 0.8))
mu4e-headers-new-mark `("N" . ,(all-the-icons-faicon "rss" :height 0.8))
mu4e-headers-passed-mark `("P" . ,(all-the-icons-faicon "check" :height 0.8))
mu4e-headers-replied-mark `("R" . ,(all-the-icons-faicon "reply" :height 0.8))
mu4e-headers-seen-mark `("S" . ,(all-the-icons-faicon "eye" :height 0.8))
mu4e-headers-unread-mark `("u" . ,(all-the-icons-faicon "eye-slash" :height 0.8))
mu4e-headers-trashed-mark `("T" . ,(all-the-icons-faicon "trash" :height 0.8))
mu4e-headers-attach-mark `("a" . ,(all-the-icons-faicon "paperclip" :height 0.8))
mu4e-headers-encrypted-mark `("x" . ,(all-the-icons-faicon "lock" :height 0.8))
mu4e-headers-signed-mark `("s" . ,(all-the-icons-faicon "certificate" :height 0.8))
#+end_example
Lets enable them and set them:
#+name: mu4e-fancy-marks
#+begin_src emacs-lisp :tangle no
;; Unbinding some stuff
(setq mu4e-use-fancy-chars t
<<mu4e-fancy-marks-gen()>>)
#+end_src
***** Headers mode
#+name: mu4e-headers-mode
#+begin_src emacs-lisp
(add-hook 'mu4e-headers-mode-hook (lambda () (visual-line-mode -1)))
(add-hook 'mu4e-headers-mode-hook (lambda () (toggle-truncate-lines -1)))
#+end_src
***** Keybindings
By default, Evil has some pretty annoying keybindings for users of the
bépo layout: ~hjkl~ becomes ~ctsr~ for us. Lets undefine some of these:
#+name: mu4e-keybindings-undef
#+begin_src emacs-lisp
(general-define-key
:keymaps '(mu4e-headers-mode-map mu4e-view-mode-map)
"s" nil)
(general-define-key
:states 'normal
:keymaps '(mu4e-headers-mode-map mu4e-view-mode-map)
"s" nil)
"s" nil
"," nil)
(general-define-key
:keymaps 'mu4e-view-mode-map
"SPC" nil
"S" nil
"r" nil
"c" nil)
(general-define-key
:keymaps 'mu4e-view-mode-map
:states 'normal
@ -913,119 +1118,184 @@ grow the ~mu4e-headers~ buffer when viewing an email.
"r" nil
"c" nil
"gu" nil)
#+end_src
;; View
(general-define-key
:keymaps 'mu4e-view-mode-map
:states 'normal
)
#+RESULTS: mu4e-keybindings-undef
(general-define-key
:states 'motion
:keymaps 'mu4e-view-mode-map
:prefix ","
"|" #'mu4e-view-pipe
"." '(mu4e-headers-split-adjust-width/body :wk "mu4e-headers width")
Now, lets define some keybindings for mu4es view mode, that is when
we are viewing an email. All these keybindings will reside between the
major-mode specific leader key ~,~ and most of these keybindings can be
described with a simple function:
#+name: mu4e-keybindings-view-tbl
| Keybinding | Function | Description |
|------------+--------------------------------------+--------------------|
| & | mu4e-view-pipe | |
| . | mu4e-headers-split-adjust-width/body | mu4e-headers width |
| a | nil | attachments |
| a& | mu4e-view-pipe-attachment | |
| aa | mu4e-view-attachment-action | |
| ao | mu4e-view-open-attachment | |
| aO | mu4e-view-open-attachment-with | |
| c | nil | compose |
| cc | mu4e-compose-new | |
| ce | mu4e-compose-edit | |
| cf | mu4e-compose-forward | |
| cr | mu4e-compose-reply | |
| cR | mu4e-compose-resend | |
| g | nil | go to |
| gu | mu4e-view-go-to-url | |
| gX | mu4e-view-fetch-url | |
| l | mu4e-show-log | |
| m | nil | mark |
| md | mu4e-view-mark-for-trash | |
| mD | mu4e-view-mark-for-delete | |
| mm | mu4e-view-mark-for-move | |
| mr | mu4e-view-mark-for-refile | |
| mR | mu4e-view-mark-for-read | |
| mu | mu4e-view-mark-for-unread | |
| mU | mu4e-view-mark-for-unmark | |
| t | nil | thread |
| T | nil | toggle |
| Tc | mu4e-view-toggle-hide-cited | |
| Th | mu4e-view-toggle-html | |
| n | mu4e-view-headers-next | |
| N | mu4e-view-headers-next-unread | |
| p | mu4e-view-headers-prev | |
| P | mu4e-view-headers-prev-unread | |
"a" '(nil :wk "attachments")
"a|" #'mu4e-view-pipe-attachment
"aa" #'mu4e-view-attachment-action
"ao" #'mu4e-view-open-attachment
"aO" #'mu4e-view-open-attachment-with
Some functions are however lambdas. They all are written as actions on
the current thread. They all begin with ~,t~, and below you can see each
one of the possible following key can act on a thread:
#+name: mu4e-keybindings-view-lambdas-tbl
| Key | Mark thread as |
|-----+----------------|
| td | trash |
| tD | delete |
| tm | move |
| tr | refile |
| tR | read |
| tu | unread |
| tU | unmark |
"c" '(nil :wk "compose")
"cc" #'mu4e-compose-new
"ce" #'mu4e-compose-edit
"cf" #'mu4e-compose-forward
"cr" #'mu4e-compose-reply
"cR" #'mu4e-compose-resend
"g" '(nil :wk "go to")
"gu" #'mu4e-view-go-to-url
"gX" #'mu4e-view-fetch-url
"l" #'mu4e-show-log
"m" '(nil :wk "mark")
"md" #'mu4e-view-mark-for-trash
"mD" #'mu4e-view-mark-for-delete
"mm" #'mu4e-view-mark-for-move
"mr" #'mu4e-view-mark-for-refile
"mR" #'mu4e-view-mark-for-read
"mu" #'mu4e-view-mark-for-unread
"mU" #'mu4e-view-mark-for-unmark
"t" '(nil :wk "thread")
"td" '((lambda ()
#+name: mu4e-keybindings-view-lambdas-gen
#+header: :tangle no :exports none :results value
#+begin_src emacs-lisp :var table=mu4e-keybindings-view-lambdas-tbl mode="view"
(mapconcat (lambda (line)
(let ((key (car line))
(mark (cadr line)))
(format "\"%s\" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(trash)))
(mu4e-%s-mark-thread '%s))
:wk \"Mark as %s\")"
key mode mark mark)))
table
"\n")
#+end_src
#+RESULTS: mu4e-keybindings-view-lambdas-gen
#+begin_example
"td" '((lambda ()
(interactive)
(mu4e-view-mark-thread 'trash))
:wk "Mark as trash")
"tD" '((lambda ()
"tD" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(delete)))
(mu4e-view-mark-thread 'delete))
:wk "Mark as delete")
"tm" '((lambda ()
"tm" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(move)))
(mu4e-view-mark-thread 'move))
:wk "Mark as move")
"tr" '((lambda ()
"tr" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(refile)))
(mu4e-view-mark-thread 'refile))
:wk "Mark as refile")
"tR" '((lambda ()
"tR" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(read)))
(mu4e-view-mark-thread 'read))
:wk "Mark as read")
"tu" '((lambda ()
"tu" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(unread)))
(mu4e-view-mark-thread 'unread))
:wk "Mark as unread")
"tU" '((lambda ()
"tU" '((lambda ()
(interactive)
(mu4e-view-mark-thread '(unmark)))
(mu4e-view-mark-thread 'unmark))
:wk "Mark as unmark")
#+end_example
"T" '(nil :wk "toggle")
"Tc" #'mu4e-view-toggle-hide-cited
"Th" #'mu4e-view-toggle-html
"n" #'mu4e-view-headers-next
"N" #'mu4e-view-headers-next-unread
"p" #'mu4e-view-headers-prev
"P" #'mu4e-view-headers-prev-unread)
;; Headers
#+name: mu4e-keybindings-view
#+begin_src emacs-lisp
(general-define-key
:states 'normal
:keymaps 'mu4e-view-mode-map
:prefix ","
<<general-keybindings-gen(table=mu4e-keybindings-view-tbl)>>
<<mu4e-keybindings-view-lambdas-gen(mode="view")>>)
#+end_src
Some simple keybindings are defined for mu4es header mode:
#+name: mu4e-keybindings-headers-tbl
| Key | Function | Description |
|-----+----------+-------------|
| s | swiper | |
| t | nil | thread |
The keybindings from table [[mu4e-keybindings-view-lambdas-tbl]] will also
be reused for this mode.
#+name: mu4e-keybindings-header
#+begin_src emacs-lisp
(general-define-key
:states 'normal
:keymaps 'mu4e-headers-mode-map
:prefix ","
<<general-keybindings-gen(table=mu4e-keybindings-headers-tbl)>>
<<mu4e-keybindings-view-lambdas-gen(mode="headers")>>)
#+end_src
#+RESULTS: mu4e-keybindings-header
I will also redefine without a leader key ~ctsr~ in order to be able to
move freely (remember, bépo layout for me).
#+name: mu4e-keybindings-header-no-leader
#+begin_src emacs-lisp
(general-define-key
:keymaps 'mu4e-headers-mode-map
:states 'normal
"s" '(nil :wk "search")
"ss" #'swiper)
(general-define-key
:keymaps 'mu4e-headers-mode-map
:states 'motion
"c" #'evil-backward-char
"t" #'evil-next-line
"s" #'evil-previous-line
"T" '((lambda ()
(interactive)
(mu4e-headers-mark-thread nil '(read)))
:wk "Mark as read"))
"r" #'evil-forward-char)
#+end_src
;; Message
#+RESULTS: mu4e-keybindings-header-no-leader
Finally, lets declare a couple of keybindings for when we are
composing a message. This time, all my keybindings are prefixed with
the major-mode leader and call a simple function.
#+name: mu4e-keybindings-message-tbl
| Key | Function | Description |
|-----+-----------------------+-------------|
| , | message-send-and-exit | |
| c | message-send-and-exit | |
| a | message-kill-buffer | |
| k | message-kill-buffer | |
| s | message-dont-send | |
| f | mml-attach-file) | |
#+name: mu4e-keybindings-message
#+begin_src emacs-lisp :tangle no
(general-define-key
:states 'normal
:keymaps 'message-mode-map
:prefix ","
"," #'message-send-and-exit
"c" #'message-send-and-exit
"a" #'message-kill-buffer
"k" #'message-kill-buffer
"s" #'message-dont-send
"f" #'mml-attach-file)
<<general-keybindings-gen(table=mu4e-keybindings-message-tbl)>>
#+end_src
#+RESULTS: mu4e-keybindings-message
**** Composing messages
#+begin_src emacs-lisp
(use-package org-msg
:after (org mu4e)