355 lines
14 KiB
EmacsLisp
355 lines
14 KiB
EmacsLisp
;;; mail-settings.el --- Mail settings -*- lexical-binding: t -*-
|
|
|
|
;;; Commentary:
|
|
;; https://kkatsuyuki.github.io/notmuch-conf/
|
|
;; https://opensource.com/article/20/1/organize-email-notmuch
|
|
;; https://notmuchmail.org/emacstips/
|
|
;; https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-2-notmuch-and-emacs/
|
|
|
|
;; `mm-text-html-renderer'
|
|
|
|
;; `mailcap-parse-mailcaps' in mailcap.el uses a given path or the
|
|
;; value of environment variable MAILCAPS if set or either
|
|
;; ("~/.mailcap", "/etc/mailcap", "/usr/etc/mailcap",
|
|
;; "/usr/local/etc/mailcap") for *nix or ("~/.mailcap", "~/mail.cap",
|
|
;; "~/etc/mail.cap") for `mailcap-poor-system-types'. parsed
|
|
;; mailcaps, see `mailcap-parsed-p', are in `mailcap-mime-data'.
|
|
|
|
;; `notmuch-show-view-part' `mm-inlined-types'
|
|
;; `notmuch-show-attachment-debug'
|
|
|
|
;;; Requirments:
|
|
;; (mu / mu4e)
|
|
;; mu4e-maildirs-extension https://melpa.org/#/mu4e-maildirs-extension https://github.com/agpchil/mu4e-maildirs-extension https://www.djcbsoftware.nl/code/mu/mu4e/
|
|
;; (notmuch)
|
|
|
|
;;; Code:
|
|
(use-package message
|
|
:defer t
|
|
:config
|
|
(set-face-attribute 'message-header-name nil :foreground "#4f97d7")
|
|
(set-face-attribute 'message-header-subject nil :foreground "#bc6ec5")
|
|
(set-face-attribute 'message-header-to nil :foreground "#c56ec3")
|
|
(set-face-attribute 'message-header-cc nil :foreground "#c56ec3")
|
|
(set-face-attribute 'message-header-other nil :foreground "#ce537a")
|
|
(set-face-attribute 'message-cited-text nil :foreground "#9f8766")
|
|
(set-face-attribute 'message-mml nil :foreground "#86dc2f") ;; mime meta language
|
|
;; define in custom.el user-mail-address, user-full-name
|
|
(setq message-directory "~/.local/share/mail/") ;; sent mails
|
|
(setq message-auto-save-directory "~/.local/share/mail/drafts/") ;; drafts
|
|
(setq message-kill-buffer-on-exit t) ;; after sending mail
|
|
(setq mm-text-html-renderer 'shr))
|
|
|
|
(defgroup my-mu4e nil
|
|
"My mu4e concept mapping"
|
|
:prefix "my-mu4e-"
|
|
:group 'my)
|
|
|
|
(defcustom my-mu4e-sync 'test
|
|
"One of `test', `offlineimap' and `mbsync'.
|
|
see also `my-mu4e-offlineimap-command' and `my-mu4e-mbsync-command'.
|
|
|
|
`mu4e-get-mail-command' and `mu4e-change-filenames-when-moving' will
|
|
be set according to this variable."
|
|
:group 'my-mu4e
|
|
:type '(choice
|
|
symbol
|
|
(const :tag "test" test)
|
|
(const :tag "offlineimap" offlineimap)
|
|
(const :tag "mbsync" mbsync)))
|
|
|
|
(defcustom my-mu4e-offlineimap-command "offlineimap"
|
|
"Oflineimap command used for mu4e if set in `my-mu4e-sync'."
|
|
:group 'my-mu4e
|
|
:type '(choice string (const :tag "offlineimap" "offlineimap")))
|
|
|
|
(defcustom my-mu4e-mbsync-command "mbsync"
|
|
"mbsync command used for mu4e if set in `my-mu4e-sync'."
|
|
:group 'my-mu4e
|
|
:type '(choice string (const :tag "mbsync" "mbsync")))
|
|
|
|
(defcustom my-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))
|
|
"Documention see `mu4e-bookmarks' in `mu4e-vars.el'.
|
|
|
|
:query same as \"mu find xxx\""
|
|
:group 'my-mu4e
|
|
:type '(repeat (plist)))
|
|
|
|
(defcustom my-mu4e-attachment-dir (expand-file-name "~/")
|
|
"Documention see `mu4e-attachment-dir' in `mu4e-vars.el'."
|
|
:group 'my-mu4e
|
|
:type '(choice
|
|
directory
|
|
(const :tag "~/" "~/")
|
|
(const :tag "~/Downloads" "~/Downloads"))
|
|
:safe 'stringp)
|
|
|
|
(defcustom my-mu4e-contexts nil
|
|
"Documention see `mu4e-contexts' in `mu4e-context.el'."
|
|
:group 'my-mu4e
|
|
:type '(list))
|
|
|
|
(defcustom my-mu4e-user-mailing-lists nil
|
|
"Documention see `mu4e-user-mailing-lists' in `mu4e-lists.el'."
|
|
:group 'my-mu4e
|
|
:type '(repeat (cons string string)))
|
|
|
|
;; note `mu4e-headers-results-limit'
|
|
;; headers view
|
|
;; [/] `mu4e-headers-search-narrow' - filter messages in current search query (x AND y)
|
|
;; [\] `mu4e-headers-query-prev' - go back to previous search query
|
|
(use-package mu4e
|
|
:delight
|
|
(mu4e-main-mode "🅜m")
|
|
(mu4e-headers-mode "🅜h") ;; will be overwitten with `mu4e~headers-search-execute'
|
|
(mu4e-view-mode "🅜v")
|
|
(mu4e~update-mail-mode "🅜u")
|
|
:commands mu4e
|
|
:config
|
|
;; allow to display emoji in the subject lines of the emails
|
|
(delete 'mu4e-headers-mode emojify-inhibit-major-modes) ;; https://github.com/iqbalansari/emacs-emojify/issues/39
|
|
|
|
;; set `mu4e-get-mail-command' (runs mu index afterwards, by default key U in main or C-c C-u else)
|
|
;; and `mu4e-change-filenames-when-moving'
|
|
;; no default cond for custom settings
|
|
(cond ((eq my-mu4e-sync 'test)
|
|
"reset to saved (`custom-set-variables') or standard (if not saved) value"
|
|
(custom-reevaluate-setting 'mu4e-get-mail-command) ;; true for testing
|
|
(custom-reevaluate-setting 'mu4e-change-filenames-when-moving))
|
|
((eq my-mu4e-sync 'offlineimap)
|
|
(setq mu4e-get-mail-command my-mu4e-offlineimap-command)
|
|
(setq mu4e-change-filenames-when-moving nil)) ;; nil better for offlineimap
|
|
((eq my-mu4e-sync 'mbsync)
|
|
(setq mu4e-get-mail-command my-mu4e-mbsync-command)
|
|
(setq mu4e-change-filenames-when-moving t))) ;; t better for mbsync
|
|
|
|
;;; vars
|
|
(setq mu4e-completing-read-function 'ivy-completing-read)
|
|
(setq mu4e-confirm-quit nil)
|
|
(setq mu4e-use-fancy-chars nil) ;; use unicodes for fancy tags etc. But this does not means emojis are activated.
|
|
(setq mu4e-bookmarks my-mu4e-bookmarks)
|
|
(setq mu4e-attachment-dir my-mu4e-attachment-dir)
|
|
|
|
;;; headers
|
|
;;(setq mu4e-headers-results-limit 500) ;; to ignore the limit for one query press a C-u before invoking the search. consider narrowing the search instead of increasing the limit number
|
|
(setq mu4e-headers-fields '((:human-date . 12)
|
|
(:flags . 6)
|
|
(:mailing-list . 10)
|
|
;;(:from . 22)
|
|
(:from-or-to . 22) ;; :from-or-to shows From: except when the e-mail was sent by the user (i.e., you) in that case it shows To: (prefixed by To, see also `mu4e-headers-from-or-to-prefix')
|
|
(:subject)))
|
|
;;(setq mu4e-headers-from-or-to-prefix '("" . "To ")) ;; prefix "To " for sent messages
|
|
|
|
;;; message
|
|
;;(setq mu4e-html2text-command ...)
|
|
|
|
;;; view
|
|
(setq mu4e-view-show-images t)
|
|
(setq mu4e-view-show-addresses t)
|
|
(add-to-list 'mu4e-headers-actions '("view in browser" . mu4e-action-view-in-browser))
|
|
(add-to-list 'mu4e-view-actions '("view in browser" . mu4e-action-view-in-browser))
|
|
|
|
;;; actions
|
|
(setq mu4e-action-tags-header "X-Keywords") ;; for e.g. gmail
|
|
|
|
;;; compose
|
|
;; use mu4e for e-mail in emacs
|
|
(setq mail-user-agent 'mu4e-user-agent) ;; for completion for To, CC, BSS fields
|
|
;; (setq mu4e-sent-messages-behavior 'delete) ;; TODO: sent is automatic done via server? therefore delete?
|
|
|
|
;;; draft
|
|
;;(defalias 'mu4e-compose-signature 'message-signature) ;; point to `message-signature'
|
|
(setq mu4e-compose-signature-auto-include nil) ;; see also `mu4e-compose-signature defined in contexts
|
|
|
|
;;; context
|
|
;; switch context with M-x mu4e-context-switch (force reload (if same context) with prefix-argument C-u)
|
|
;; see current context with M-x mu4e-context-current
|
|
(setq mu4e-contexts my-mu4e-contexts)
|
|
(defun my-mu4e-message-maildir-matches (msg rx)
|
|
"helper to determine which account context to use based on the
|
|
folder in maildir the email (eg. ~/.mail/work) is located in."
|
|
(when rx
|
|
(if (listp rx)
|
|
;; If rx is a list, try each one for a match
|
|
(or (mu4e-message-maildir-matches msg (car rx))
|
|
(mu4e-message-maildir-matches msg (cdr rx)))
|
|
;; Not a list, check rx
|
|
(string-match rx (mu4e-message-field msg :maildir)))))
|
|
;; overwite in custom.el
|
|
(defun my-mu4e-refile-folder-work (msg)
|
|
"Function for chosing the refile folder for the work account.
|
|
MSG is a message p-list from mu4e."
|
|
(cond
|
|
;; nil no refiling
|
|
))
|
|
|
|
;; set `mu4e-context-policy` and `mu4e-compose-policy` to tweak when mu4e should
|
|
;; guess or ask the correct context, e.g.
|
|
(setq mu4e-context-policy 'pick-first) ;; start with the first (default) context; default is to ask-if-none (ask when there's no context yet, and none match)
|
|
;; (setq mu4e-compose-context-policy nil) ;; compose with the current context is no context matches; default is to ask
|
|
|
|
;;; lists
|
|
;; The shortname (friendly) should a at most 8 characters, camel-case, see also `mu4e-mailing-list-patterns'
|
|
(setq mu4e-user-mailing-lists my-mu4e-user-mailing-lists)
|
|
|
|
;;; org
|
|
;;(setq mu4e-org-link-query-in-headers-mode nil) ;; point to message (default) rather then the query
|
|
)
|
|
|
|
(use-package mu4e-maildirs-extension
|
|
:after mu4e
|
|
:config
|
|
(setq mu4e-maildirs-extension-title
|
|
(concat " Maildirs\t(["
|
|
(key-description mu4e-maildirs-extension-toggle-maildir-key)
|
|
"] fold, [C-u "
|
|
(key-description mu4e-maildirs-extension-toggle-maildir-key)
|
|
"] fold w/ children, [RET]/[mouse-2] jump to, [C-u RET] jump to unread only)\n"))
|
|
(setq mu4e-maildirs-extension-action-text
|
|
(concat "\t* ["
|
|
(key-description mu4e-maildirs-extension-action-key)
|
|
"]pdate index & cache\t([C-u "
|
|
(key-description mu4e-maildirs-extension-action-key)
|
|
"] clear cache, [C-u C-u "
|
|
(key-description mu4e-maildirs-extension-action-key)
|
|
"] clear and refresh)\n"))
|
|
(setq mu4e-maildirs-extension-maildir-default-prefix "└") ;;├> └>
|
|
(setq mu4e-maildirs-extension-fake-maildir-separator "\\.")
|
|
|
|
;; overwrite to remove double entries:
|
|
;; /desy/*
|
|
;; /desy/Drafts
|
|
;; /desy/INBOX
|
|
;; /desy/INBOX.event
|
|
;; /desy/INBOX.Ing
|
|
;; /desy/INBOX/Ing/*
|
|
;; /desy/INBOX.Ing.CAD
|
|
;; /desy/INBOX/Ing/CAD/*
|
|
;; alteration: searching for "/.*/.*" pathes and do not append /* to them.
|
|
(defun mu4e-maildirs-extension-paths ()
|
|
"Get maildirs paths."
|
|
(let ((paths (mu4e-maildirs-extension-get-relevant-maildirs))
|
|
(paths-to-show nil))
|
|
|
|
(mapc #'(lambda (name)
|
|
(let ((parents (butlast (mu4e-maildirs-extension-parse name)))
|
|
(path nil))
|
|
(mapc #'(lambda (parent-name)
|
|
(setq path (concat path "/" parent-name))
|
|
(unless (or (member path paths-to-show)
|
|
(string-match-p "/.*/.*" path))
|
|
(add-to-list 'paths-to-show (format "%s/*" path) t)))
|
|
parents))
|
|
(add-to-list 'paths-to-show name t))
|
|
paths)
|
|
paths-to-show))
|
|
|
|
(mu4e-maildirs-extension))
|
|
|
|
(defgroup my-notmuch nil
|
|
"My notmuch concept mapping"
|
|
:prefix "my-notmuch-"
|
|
:group 'my)
|
|
|
|
(defcustom my-notmuch-tag-formats
|
|
'(("unread" (propertize tag 'face 'notmuch-tag-unread))
|
|
("flagged" (propertize tag 'face 'notmuch-tag-flagged)
|
|
(notmuch-tag-format-image-data tag (notmuch-tag-star-icon))))
|
|
"Documention see `notmuch-tag-formats' in `notmuch-tag.el'."
|
|
:group 'my-notmuch
|
|
:type 'notmuch-tag-format-type)
|
|
|
|
(defcustom my-notmuch-saved-searches
|
|
`((:name "inbox" :query "tag:inbox" :key ,(kbd "i"))
|
|
(:name "unread" :query "tag:unread" :key ,(kbd "u"))
|
|
(:name "flagged" :query "tag:flagged" :key ,(kbd "f"))
|
|
(:name "sent" :query "tag:sent" :key ,(kbd "t"))
|
|
(:name "drafts" :query "tag:draft" :key ,(kbd "d"))
|
|
(:name "all mail" :query "*" :key ,(kbd "a")))
|
|
"Documention see `notmuch-saved-searches' in `notmuch-hello.el'."
|
|
:group 'my-notmuch
|
|
:get 'notmuch-hello--saved-searches-to-plist
|
|
:type '(repeat notmuch-saved-search-plist)
|
|
:tag "List of Saved Searches")
|
|
|
|
(use-package notmuch
|
|
:delight
|
|
(notmuch-hello-mode "🅝h")
|
|
(notmuch-message-mode "🅝m")
|
|
(notmuch-search-mode "🅝s")
|
|
(notmuch-show-mode "🅝w")
|
|
(notmuch-tree-mode "🅝t")
|
|
:commands notmuch
|
|
:config
|
|
(set-face-attribute 'notmuch-tag-flagged nil :foreground "#b1951d")
|
|
(setq notmuch-tag-formats my-notmuch-tag-formats)
|
|
(setq notmuch-search-oldest-first nil) ;; newest first
|
|
(setq notmuch-saved-searches my-notmuch-saved-searches)
|
|
|
|
(add-to-list 'notmuch-hello-sections 'my-notmuch-hello-insert-footer t)
|
|
(defun my-notmuch-hello-insert-footer ()
|
|
"Insert the notmuch-hello footer."
|
|
(widget-insert "\n")
|
|
(let ((start (point))) ;; to center text
|
|
(widget-insert "To download mails execute ")
|
|
(if (executable-find "offlineimap")
|
|
(widget-create 'link
|
|
:notify (lambda (&rest ignore)
|
|
(my-mail-exec-offlineimap))
|
|
:button-prefix "" :button-suffix ""
|
|
:help-echo "my-mail-exec-offlineimap"
|
|
"OfflineIMAP")
|
|
(widget-create 'link
|
|
:notify (lambda (&rest ignore)
|
|
(message "offlineimap is not installed"))
|
|
:button-prefix "" :button-suffix ""
|
|
:button-face 'error
|
|
:help-echo "offlineimap is not installed"
|
|
"OfflineIMAP"))
|
|
(widget-insert " (also using ")
|
|
(if (executable-find "notmuch")
|
|
(widget-insert "notmuch")
|
|
(widget-insert (propertize "notmuch" 'face 'error)))
|
|
(widget-insert " and ")
|
|
(if (executable-find "afew")
|
|
(widget-insert "afew")
|
|
(widget-insert (propertize "afew" 'face 'error)))
|
|
(widget-insert ")")
|
|
(let ((fill-column (- (window-width) notmuch-hello-indent)))
|
|
(center-region start (point)))))
|
|
|
|
(defun my-mail-exec-offlineimap ()
|
|
"execute offlineimap"
|
|
(interactive)
|
|
(set-process-sentinel
|
|
(start-process-shell-command "offlineimap"
|
|
"*offlineimap*"
|
|
"offlineimap -o")
|
|
'(lambda (process event)
|
|
(notmuch-refresh-all-buffers)
|
|
(let ((w (get-buffer-window "*offlineimap*")))
|
|
(when w
|
|
(with-selected-window w (recenter (window-end)))))))
|
|
(popwin:display-buffer "*offlineimap*"))
|
|
;; add a special buffer config
|
|
(add-to-list 'popwin:special-display-config
|
|
'("*offlineimap*" :dedicated t :position bottom :stick t
|
|
:height 0.4 :noselect t))
|
|
)
|
|
|
|
(provide 'mail-settings)
|
|
;;; mail-settings.el ends here
|