Files
emacs/settings/mail-settings.el

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