Add email/{wanderlust,mu4e,notmuch} modules
Removed app/{email,notmuch}
This commit is contained in:
parent
45266213a5
commit
84c5da844b
16 changed files with 178 additions and 89 deletions
91
modules/email/mu4e/README.org
Normal file
91
modules/email/mu4e/README.org
Normal file
|
@ -0,0 +1,91 @@
|
|||
#+TITLE: email/mu4e
|
||||
#+DATE: April 8, 2017
|
||||
#+SINCE: v2.0
|
||||
#+STARTUP: inlineimages
|
||||
|
||||
* Table of Contents :TOC:
|
||||
- [[Description][Description]]
|
||||
- [[Module Flags][Module Flags]]
|
||||
- [[Plugins][Plugins]]
|
||||
- [[Prerequisites][Prerequisites]]
|
||||
- [[MacOS][MacOS]]
|
||||
- [[Arch Linux][Arch Linux]]
|
||||
- [[Features][Features]]
|
||||
- [[Configuration][Configuration]]
|
||||
- [[offlineimap][offlineimap]]
|
||||
- [[mbsync][mbsync]]
|
||||
|
||||
* Description
|
||||
This module makes Emacs an email client, using ~mu4e~.
|
||||
|
||||
#+begin_quote
|
||||
I want to live in Emacs, but as we all know, living is incomplete without email.
|
||||
So I prayed to the text editor gods and they (I) answered. Emacs+evil's editing
|
||||
combined with org-mode for writing emails? /Yes please./
|
||||
|
||||
It uses ~mu4e~ to read my email, but depends on ~offlineimap~ (to sync my email
|
||||
via IMAP) and ~mu~ (to index my mail into a format ~mu4e~ can understand).
|
||||
#+end_quote
|
||||
|
||||
** Module Flags
|
||||
+ ~+gmail~ Enables gmail-specific configuration.
|
||||
|
||||
** Plugins
|
||||
+ [[https://github.com/agpchil/mu4e-maildirs-extension][mu4e-maildirs-extension]]
|
||||
|
||||
* Prerequisites
|
||||
This module requires:
|
||||
|
||||
+ Either ~mbsync~ (default) or ~offlineimap~ (to sync mail with)
|
||||
+ ~mu~ (to index your downloaded messages)
|
||||
|
||||
** MacOS
|
||||
#+BEGIN_SRC sh
|
||||
brew install mu --with-emacs
|
||||
# And one of the following
|
||||
brew install isync # mbsync
|
||||
brew install offlineimap
|
||||
#+END_SRC
|
||||
|
||||
** Arch Linux
|
||||
#+BEGIN_SRC sh
|
||||
sudo pacman --noconfirm --needed -S mu
|
||||
# And one of the following
|
||||
sudo pacman -S isync # mbsync
|
||||
sudo pacman -S offlineimap
|
||||
#+END_SRC
|
||||
|
||||
* TODO Features
|
||||
|
||||
* Configuration
|
||||
** offlineimap
|
||||
This module uses =mbsync= by default. To change this, change ~+mu4e-backend~:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp
|
||||
(setq +mu4e-backend 'offlineimap)
|
||||
#+END_SRC
|
||||
|
||||
Then you must set up offlineimap and index your mail:
|
||||
|
||||
1. Write a ~\~/.offlineimaprc~. Mine can be found [[https://github.com/hlissner/dotfiles/tree/master/shell/mu][in my dotfiles repository]]. It
|
||||
is configured to download mail to ~\~/.mail~. I use [[https://www.passwordstore.org/][unix pass]] to securely
|
||||
store my login credentials.
|
||||
2. Download your email: ~offlineimap -o~ (may take a while)
|
||||
3. Index it with mu: ~mu index --maildir ~/.mail~
|
||||
|
||||
Then configure Emacs to use your email address:
|
||||
|
||||
#+BEGIN_SRC emacs-lisp :tangle no
|
||||
;; Each path is relative to `+mu4e-mu4e-mail-path', which is ~/.mail by default
|
||||
(set-email-account! "Lissner.net"
|
||||
'((mu4e-sent-folder . "/Lissner.net/Sent Mail")
|
||||
(mu4e-drafts-folder . "/Lissner.net/Drafts")
|
||||
(mu4e-trash-folder . "/Lissner.net/Trash")
|
||||
(mu4e-refile-folder . "/Lissner.net/All Mail")
|
||||
(smtpmail-smtp-user . "henrik@lissner.net")
|
||||
(user-mail-address . "henrik@lissner.net")
|
||||
(mu4e-compose-signature . "---\nHenrik Lissner"))
|
||||
t)
|
||||
#+END_SRC
|
||||
|
||||
** TODO mbsync
|
78
modules/email/mu4e/autoload/email.el
Normal file
78
modules/email/mu4e/autoload/email.el
Normal file
|
@ -0,0 +1,78 @@
|
|||
;;; email/mu4e/autoload/email.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autodef
|
||||
(defun set-email-account! (label letvars &optional default-p)
|
||||
"Registers an email address for mu4e. The LABEL is a string. LETVARS are a
|
||||
list of cons cells (VARIABLE . VALUE) -- you may want to modify:
|
||||
|
||||
+ `user-full-name' (this or the global `user-full-name' is required)
|
||||
+ `user-mail-address' (required)
|
||||
+ `smtpmail-smtp-user' (required for sending mail from Emacs)
|
||||
|
||||
OPTIONAL:
|
||||
+ `mu4e-sent-folder'
|
||||
+ `mu4e-drafts-folder'
|
||||
+ `mu4e-trash-folder'
|
||||
+ `mu4e-refile-folder'
|
||||
+ `mu4e-compose-signature'
|
||||
|
||||
DEFAULT-P is a boolean. If non-nil, it marks that email account as the
|
||||
default/fallback account."
|
||||
(after! mu4e
|
||||
(when-let* ((address (cdr (assq 'user-mail-address letvars))))
|
||||
(add-to-list 'mu4e-user-mail-address-list address))
|
||||
(setq mu4e-contexts
|
||||
(cl-loop for context in mu4e-contexts
|
||||
unless (string= (mu4e-context-name context) label)
|
||||
collect context))
|
||||
(let ((context (make-mu4e-context
|
||||
:name label
|
||||
:enter-func (lambda () (mu4e-message "Switched to %s" label))
|
||||
:leave-func #'mu4e-clear-caches
|
||||
:match-func
|
||||
(lambda (msg)
|
||||
(when msg
|
||||
(string-prefix-p (format "/%s" label)
|
||||
(mu4e-message-field msg :maildir))))
|
||||
:vars letvars)))
|
||||
(push context mu4e-contexts)
|
||||
(when default-p
|
||||
(setq-default mu4e-context-current context))
|
||||
context)))
|
||||
|
||||
|
||||
|
||||
(defvar +mu4e-workspace-name "*mu4e*"
|
||||
"TODO")
|
||||
|
||||
(add-hook 'mu4e-main-mode-hook #'+mu4e|init)
|
||||
|
||||
;;;###autoload
|
||||
(defun =mu4e ()
|
||||
"Start email client."
|
||||
(interactive)
|
||||
(require 'mu4e)
|
||||
(+workspace-switch +mu4e-workspace-name t)
|
||||
(mu4e~start 'mu4e~main-view)
|
||||
;; (save-selected-window
|
||||
;; (prolusion-mail-show))
|
||||
)
|
||||
|
||||
;;;###autoload
|
||||
(defun +mu4e/compose ()
|
||||
"Compose a new email."
|
||||
(interactive)
|
||||
;; TODO Interactively select email account
|
||||
(call-interactively #'mu4e-compose-new))
|
||||
|
||||
|
||||
;;
|
||||
;; Hooks
|
||||
|
||||
(defun +mu4e|init ()
|
||||
(add-hook 'kill-buffer-hook #'+mu4e|kill-mu4e nil t))
|
||||
|
||||
(defun +mu4e|kill-mu4e ()
|
||||
;; (prolusion-mail-hide)
|
||||
(when (+workspace-exists-p +mu4e-workspace-name)
|
||||
(+workspace/delete +mu4e-workspace-name)))
|
21
modules/email/mu4e/autoload/evil.el
Normal file
21
modules/email/mu4e/autoload/evil.el
Normal file
|
@ -0,0 +1,21 @@
|
|||
;; email/mu4e/autoload/evil.el -*- lexical-binding: t; -*-
|
||||
;;;###if (featurep! :editor evil)
|
||||
|
||||
;;;###autoload
|
||||
(defun +mu4e/mark (&optional beg end)
|
||||
"Mark all messages within the current selection in mu4e's header view. Uses
|
||||
`this-command-keys' to see what flag you mean."
|
||||
(interactive)
|
||||
(let* ((beg (or beg (and (region-active-p) evil-visual-beginning) (line-beginning-position)))
|
||||
(end (or end (and (region-active-p) evil-visual-end) (line-end-position)))
|
||||
(key (this-command-keys))
|
||||
(command
|
||||
(car (cl-find-if (lambda (mark) (equal (car (plist-get (cdr mark) :char)) key))
|
||||
mu4e-marks))))
|
||||
(unless command
|
||||
(error "Not a valid mark command: %s" key))
|
||||
(when (bound-and-true-p evil-mode)
|
||||
(evil-normal-state))
|
||||
(goto-char beg)
|
||||
(dotimes (_ (count-lines beg end))
|
||||
(mu4e-headers-mark-and-next command))))
|
178
modules/email/mu4e/config.el
Normal file
178
modules/email/mu4e/config.el
Normal file
|
@ -0,0 +1,178 @@
|
|||
;;; email/mu4e/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(defvar +mu4e-backend 'mbsync
|
||||
"Which backend to use. Can either be offlineimap, mbsync or nil (manual).")
|
||||
|
||||
|
||||
;;
|
||||
;;; Packages
|
||||
|
||||
(add-to-list 'auto-mode-alist '("\\.\\(?:offlineimap\\|mbsync\\)rc\\'" . conf-mode))
|
||||
|
||||
|
||||
(def-package! mu4e
|
||||
:commands (mu4e mu4e-compose-new)
|
||||
:init
|
||||
(provide 'html2text) ; disable obsolete package
|
||||
(setq mu4e-maildir "~/.mail"
|
||||
mu4e-attachment-dir "~/.mail/.attachments"
|
||||
mu4e-user-mail-address-list nil)
|
||||
:config
|
||||
(pcase +mu4e-backend
|
||||
(`mbsync
|
||||
(setq mu4e-get-mail-command "mbsync -a"
|
||||
mu4e-change-filenames-when-moving t))
|
||||
(`offlineimap
|
||||
(setq mu4e-get-mail-command "offlineimap -o -q")))
|
||||
|
||||
(setq mu4e-update-interval nil
|
||||
mu4e-compose-format-flowed t ; visual-line-mode + auto-fill upon sending
|
||||
mu4e-view-show-addresses t
|
||||
mu4e-sent-messages-behavior 'sent
|
||||
mu4e-hide-index-messages t
|
||||
;; try to show images
|
||||
mu4e-view-show-images t
|
||||
mu4e-view-image-max-width 800
|
||||
;; configuration for sending mail
|
||||
message-send-mail-function #'smtpmail-send-it
|
||||
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 helm/ivy
|
||||
mu4e-completing-read-function
|
||||
(cond ((featurep! :completion ivy) #'ivy-completing-read)
|
||||
((featurep! :completion helm) #'completing-read)
|
||||
(t #'ido-completing-read))
|
||||
;; no need to ask
|
||||
mu4e-confirm-quit nil
|
||||
;; remove 'lists' column
|
||||
mu4e-headers-fields
|
||||
'((:account . 12)
|
||||
(:human-date . 12)
|
||||
(:flags . 4)
|
||||
(:from . 25)
|
||||
(:subject)))
|
||||
|
||||
;; set mail user agent
|
||||
(setq mail-user-agent 'mu4e-user-agent)
|
||||
|
||||
;; Use fancy icons
|
||||
(setq mu4e-headers-has-child-prefix '("+" . "")
|
||||
mu4e-headers-empty-parent-prefix '("-" . "")
|
||||
mu4e-headers-first-child-prefix '("\\" . "")
|
||||
mu4e-headers-duplicate-prefix '("=" . "")
|
||||
mu4e-headers-default-prefix '("|" . "")
|
||||
mu4e-headers-draft-mark '("D" . "")
|
||||
mu4e-headers-flagged-mark '("F" . "")
|
||||
mu4e-headers-new-mark '("N" . "")
|
||||
mu4e-headers-passed-mark '("P" . "")
|
||||
mu4e-headers-replied-mark '("R" . "")
|
||||
mu4e-headers-seen-mark '("S" . "")
|
||||
mu4e-headers-trashed-mark '("T" . "")
|
||||
mu4e-headers-attach-mark '("a" . "")
|
||||
mu4e-headers-encrypted-mark '("x" . "")
|
||||
mu4e-headers-signed-mark '("s" . "")
|
||||
mu4e-headers-unread-mark '("u" . ""))
|
||||
|
||||
;; 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"
|
||||
:function
|
||||
(lambda (msg)
|
||||
(let ((maildir (mu4e-message-field msg :maildir)))
|
||||
(format "%s" (substring maildir 1 (string-match-p "/" maildir 1)))))))
|
||||
|
||||
;; Refresh the current view after marks are executed
|
||||
(defun +mu4e*refresh (&rest _) (mu4e-headers-rerun-search))
|
||||
(advice-add #'mu4e-mark-execute-all :after #'+mu4e*refresh)
|
||||
|
||||
(when (featurep! :tools flyspell)
|
||||
(add-hook 'mu4e-compose-mode-hook #'flyspell-mode))
|
||||
|
||||
;; Wrap text in messages
|
||||
(setq-hook! 'mu4e-view-mode-hook truncate-lines nil)
|
||||
|
||||
(when (fboundp 'imagemagick-register-types)
|
||||
(imagemagick-register-types))
|
||||
|
||||
(set-evil-initial-state!
|
||||
'(mu4e-main-mode
|
||||
mu4e-view-mode
|
||||
mu4e-headers-mode
|
||||
mu4e-compose-mode
|
||||
mu4e~update-mail-mode)
|
||||
'normal))
|
||||
|
||||
|
||||
(def-package! mu4e-maildirs-extension
|
||||
:after mu4e
|
||||
:config
|
||||
(mu4e-maildirs-extension)
|
||||
(setq mu4e-maildirs-extension-title nil))
|
||||
|
||||
|
||||
(def-package! org-mu4e
|
||||
:hook (mu4e-compose-mode . org-mu4e-compose-org-mode)
|
||||
:config
|
||||
(setq org-mu4e-link-query-in-headers-mode nil
|
||||
org-mu4e-convert-to-html t)
|
||||
|
||||
;; Only render to html once. If the first send fails for whatever reason,
|
||||
;; org-mu4e would do so each time you try again.
|
||||
(add-hook! 'message-send-hook
|
||||
(setq-local org-mu4e-convert-to-html nil)))
|
||||
|
||||
|
||||
;;
|
||||
;;; Gmail integration
|
||||
|
||||
(when (featurep! +gmail)
|
||||
(after! mu4e
|
||||
;; don't save message to Sent Messages, Gmail/IMAP takes care of this
|
||||
(setq mu4e-sent-messages-behavior 'delete
|
||||
|
||||
;; don't need to run cleanup after indexing for gmail
|
||||
mu4e-index-cleanup nil
|
||||
|
||||
;; because gmail uses labels as folders we can use lazy check since
|
||||
;; messages don't really "move"
|
||||
mu4e-index-lazy-check t)
|
||||
|
||||
;; In my workflow, emails won't be moved at all. Only their flags/labels are
|
||||
;; changed. Se we redefine the trash and refile marks not to do any moving.
|
||||
;; However, the real magic happens in `+mu4e|gmail-fix-flags'.
|
||||
;;
|
||||
;; Gmail will handle the rest.
|
||||
(defun +mu4e--mark-seen (docid _msg target)
|
||||
(mu4e~proc-move docid (mu4e~mark-check-target target) "+S-u-N"))
|
||||
|
||||
(delq (assq 'delete mu4e-marks) mu4e-marks)
|
||||
(setf (alist-get 'trash mu4e-marks)
|
||||
(list :char '("d" . "▼")
|
||||
:prompt "dtrash"
|
||||
:dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg))
|
||||
:action #'+mu4e--mark-seen)
|
||||
;; Refile will be my "archive" function.
|
||||
(alist-get 'refile mu4e-marks)
|
||||
(list :char '("d" . "▼")
|
||||
:prompt "dtrash"
|
||||
:dyn-target (lambda (_target msg) (mu4e-get-trash-folder msg))
|
||||
:action #'+mu4e--mark-seen))
|
||||
|
||||
;; This hook correctly modifies gmail flags on emails when they are marked.
|
||||
;; Without it, refiling (archiving), trashing, and flagging (starring) email
|
||||
;; won't properly result in the corresponding gmail action, since the marks
|
||||
;; are ineffectual otherwise.
|
||||
(defun +mu4e|gmail-fix-flags (mark msg)
|
||||
(pcase mark
|
||||
(`trash (mu4e-action-retag-message msg "-\\Inbox,+\\Trash,-\\Draft"))
|
||||
(`refile (mu4e-action-retag-message msg "-\\Inbox"))
|
||||
(`flag (mu4e-action-retag-message msg "+\\Starred"))
|
||||
(`unflag (mu4e-action-retag-message msg "-\\Starred"))))
|
||||
(add-hook 'mu4e-mark-execute-pre-hook #'+mu4e|gmail-fix-flags)))
|
4
modules/email/mu4e/packages.el
Normal file
4
modules/email/mu4e/packages.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; email/mu4e/packages.el
|
||||
|
||||
(package! mu4e-maildirs-extension)
|
189
modules/email/notmuch/autoload.el
Normal file
189
modules/email/notmuch/autoload.el
Normal file
|
@ -0,0 +1,189 @@
|
|||
;;; email/notmuch/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defun =notmuch ()
|
||||
"Activate (or switch to) `notmuch' in its workspace."
|
||||
(interactive)
|
||||
(unless (featurep! :ui workspaces)
|
||||
(user-error ":ui workspaces is required, but disabled"))
|
||||
(condition-case-unless-debug e
|
||||
(progn
|
||||
(+workspace-switch "*MAIL*" t)
|
||||
(if-let* ((buf (cl-find-if (lambda (it) (string-match-p "^\\*notmuch" (buffer-name (window-buffer it))))
|
||||
(doom-visible-windows))))
|
||||
(select-window (get-buffer-window buf))
|
||||
(notmuch-search "tag:inbox"))
|
||||
(+workspace/display))
|
||||
('error
|
||||
(+notmuch/quit)
|
||||
(signal (car e) (cdr e)))))
|
||||
|
||||
|
||||
;;
|
||||
;; Commands
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/quit ()
|
||||
(interactive)
|
||||
;; (+popup/close (get-buffer-window "*notmuch-hello*"))
|
||||
(doom-kill-matching-buffers "^\\*notmuch")
|
||||
(+workspace/delete "*MAIL*"))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/update ()
|
||||
(interactive)
|
||||
(start-process-shell-command
|
||||
"notmuch update" nil
|
||||
(pcase +notmuch-sync-backend
|
||||
(`gmi
|
||||
(concat "cd " +notmuch-mail-folder " && gmi push && gmi pull && notmuch new && afew -a -t"))
|
||||
(`mbsync
|
||||
"mbsync -a && notmuch new && afew -a -t")
|
||||
(`mbsync-xdg
|
||||
"mbsync -c \"$XDG_CONFIG_HOME\"/isync/mbsyncrc -a && notmuch new && afew -a -t")
|
||||
(`offlineimap
|
||||
"offlineimap && notmuch new && afew -a -t"))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/search-delete ()
|
||||
(interactive)
|
||||
(notmuch-search-add-tag (list "+trash" "-inbox" "-unread"))
|
||||
(notmuch-tree-next-message))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/tree-delete ()
|
||||
(interactive)
|
||||
(notmuch-tree-add-tag (list "+trash" "-inbox" "-unread"))
|
||||
(notmuch-tree-next-message))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/search-spam ()
|
||||
(interactive)
|
||||
(notmuch-search-add-tag (list "+spam" "-inbox" "-unread"))
|
||||
(notmuch-search-next-thread))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/tree-spam ()
|
||||
(interactive)
|
||||
(notmuch-tree-add-tag (list "+spam" "-inbox" "-unread"))
|
||||
(notmuch-tree-next-message))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/open-message-with-mail-app-notmuch-tree ()
|
||||
(interactive)
|
||||
(let* ((msg-path (car (plist-get (notmuch-tree-get-message-properties) :filename)))
|
||||
(temp (make-temp-file "notmuch-message-" nil ".eml")))
|
||||
(shell-command-to-string (format "cp '%s' '%s'" msg-path temp))
|
||||
(start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch/open-message-with-mail-app-notmuch-show ()
|
||||
(interactive)
|
||||
(let* ((msg-path (car (plist-get (notmuch-show-get-message-properties) :filename)))
|
||||
(temp (make-temp-file "notmuch-message-" nil ".eml")))
|
||||
(shell-command-to-string (format "cp '%s' '%s'" msg-path temp))
|
||||
(start-process-shell-command "email" nil (format "xdg-open '%s'" temp))))
|
||||
|
||||
|
||||
;;
|
||||
;; Advice
|
||||
|
||||
;;;###autoload
|
||||
(defun +notmuch*dont-confirm-on-kill-process (orig-fn &rest args)
|
||||
"Don't prompt for confirmation when killing notmuch sentinel."
|
||||
(let (confirm-kill-processes)
|
||||
(apply orig-fn args)))
|
||||
|
||||
;; (defun +notmuch*hello-insert-searches (title query-list &rest options)
|
||||
;; (widget-insert (propertize title 'face 'org-agenda-structure))
|
||||
;; (if (and notmuch-hello-first-run (plist-get options :initially-hidden))
|
||||
;; (add-to-list 'notmuch-hello-hidden-sections title))
|
||||
;; (let ((is-hidden (member title notmuch-hello-hidden-sections))
|
||||
;; (widget-push-button-prefix "")
|
||||
;; (widget-push-button-suffix "")
|
||||
;; (start (point)))
|
||||
;; (if is-hidden
|
||||
;; (widget-create 'push-button
|
||||
;; :notify `(lambda (widget &rest ignore)
|
||||
;; (setq notmuch-hello-hidden-sections
|
||||
;; (delete ,title notmuch-hello-hidden-sections))
|
||||
;; (notmuch-hello-update))
|
||||
;; (propertize " +" 'face 'org-agenda-structure))
|
||||
;; (widget-create 'push-button
|
||||
;; :notify `(lambda (widget &rest ignore)
|
||||
;; (add-to-list 'notmuch-hello-hidden-sections
|
||||
;; ,title)
|
||||
;; (notmuch-hello-update))
|
||||
;; " -"))
|
||||
;; (widget-insert "\n")
|
||||
;; (when (not is-hidden)
|
||||
;; (let ((searches (apply 'notmuch-hello-query-counts query-list options)))
|
||||
;; (when (or (not (plist-get options :hide-if-empty))
|
||||
;; searches)
|
||||
;; (widget-insert "\n")
|
||||
;; (notmuch-hello-insert-buttons searches)
|
||||
;; (indent-rigidly start (point) notmuch-hello-indent))))))
|
||||
|
||||
;; (defun +notmuch*hello-insert-saved-searches ()
|
||||
;; "Insert the saved-searches section."
|
||||
;; (let ((searches (notmuch-hello-query-counts
|
||||
;; (if notmuch-saved-search-sort-function
|
||||
;; (funcall notmuch-saved-search-sort-function
|
||||
;; notmuch-saved-searches)
|
||||
;; notmuch-saved-searches)
|
||||
;; :show-empty-searches notmuch-show-empty-saved-searches)))
|
||||
;; (when searches
|
||||
;; (widget-insert (propertize "Notmuch" 'face 'org-agenda-date-today))
|
||||
;; (widget-insert "\n\n")
|
||||
;; (widget-insert (propertize "Saved searches" 'face 'org-agenda-structure))
|
||||
;; (widget-insert "\n\n")
|
||||
;; (let ((start (point)))
|
||||
;; (notmuch-hello-insert-buttons searches)
|
||||
;; (indent-rigidly start (point) notmuch-hello-indent)))))
|
||||
|
||||
;; (defun +notmuch*hello-insert-buttons (searches)
|
||||
;; (let* ((widest (notmuch-hello-longest-label searches))
|
||||
;; (tags-and-width (notmuch-hello-tags-per-line widest))
|
||||
;; (tags-per-line (car tags-and-width))
|
||||
;; (column-width (cdr tags-and-width))
|
||||
;; (column-indent 0)
|
||||
;; (count 0)
|
||||
;; (reordered-list (notmuch-hello-reflect searches tags-per-line))
|
||||
;; ;; Hack the display of the buttons used.
|
||||
;; (widget-push-button-prefix "")
|
||||
;; (widget-push-button-suffix ""))
|
||||
;; ;; dme: It feels as though there should be a better way to
|
||||
;; ;; implement this loop than using an incrementing counter.
|
||||
;; (mapc (lambda (elem)
|
||||
;; ;; (not elem) indicates an empty slot in the matrix.
|
||||
;; (when elem
|
||||
;; (if (> column-indent 0)
|
||||
;; (widget-insert (make-string column-indent ? )))
|
||||
;; (let* ((name (plist-get elem :name))
|
||||
;; (query (plist-get elem :query))
|
||||
;; (oldest-first (case (plist-get elem :sort-order)
|
||||
;; (newest-first nil)
|
||||
;; (oldest-first t)
|
||||
;; (otherwise notmuch-search-oldest-first)))
|
||||
;; (search-type (eq (plist-get elem :search-type) 'tree))
|
||||
;; (msg-count (plist-get elem :count)))
|
||||
;; (widget-insert (format "\n%5s "
|
||||
;; (notmuch-hello-nice-number msg-count)))
|
||||
;; (widget-create 'push-button
|
||||
;; :notify #'notmuch-hello-widget-search
|
||||
;; :notmuch-search-terms query
|
||||
;; :notmuch-search-oldest-first oldest-first
|
||||
;; :notmuch-search-type search-type
|
||||
;; name)
|
||||
;; (setq column-indent
|
||||
;; (1+ (max 0 (- column-width (length name)))))))
|
||||
;; (setq count (1+ count))
|
||||
;; (when (eq (% count tags-per-line) 0)
|
||||
;; (setq column-indent 0)
|
||||
;; (widget-insert "\n")))
|
||||
;; reordered-list)
|
||||
|
||||
;; ;; If the last line was not full (and hence did not include a
|
||||
;; ;; carriage return), insert one now.
|
||||
;; (unless (eq (% count tags-per-line) 0)
|
||||
;; (widget-insert "\n"))))
|
74
modules/email/notmuch/config.el
Normal file
74
modules/email/notmuch/config.el
Normal file
|
@ -0,0 +1,74 @@
|
|||
;;; email/notmuch/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; FIXME This module is a WIP!
|
||||
|
||||
(defvar +notmuch-sync-backend 'gmi
|
||||
"Which backend to use. Can be either gmi, mbsync, offlineimap or nil (manual).")
|
||||
|
||||
(defvar +notmuch-mail-folder "~/.mail/account.gmail"
|
||||
"Where your email folder is located (for use with gmailieer).")
|
||||
|
||||
(after! notmuch
|
||||
(set-company-backend! 'notmuch-message-mode
|
||||
'(notmuch-company (company-ispell :with company-yasnippet)))
|
||||
|
||||
(set-popup-rule! "^\\*notmuch-hello" :side 'left :size 30 :ttl 0)
|
||||
|
||||
(setq notmuch-fcc-dirs nil
|
||||
notmuch-show-logo nil
|
||||
notmuch-message-headers-visible nil
|
||||
message-kill-buffer-on-exit t
|
||||
message-send-mail-function 'message-send-mail-with-sendmail
|
||||
notmuch-search-oldest-first nil
|
||||
send-mail-function 'sendmail-send-it
|
||||
;; sendmail-program "/usr/local/bin/msmtp"
|
||||
notmuch-search-result-format
|
||||
'(("date" . "%12s ")
|
||||
("count" . "%-7s ")
|
||||
("authors" . "%-30s ")
|
||||
("subject" . "%-72s ")
|
||||
("tags" . "(%s)"))
|
||||
notmuch-tag-formats
|
||||
'(("unread" (propertize tag 'face 'notmuch-tag-unread)))
|
||||
notmuch-hello-sections
|
||||
'(notmuch-hello-insert-saved-searches
|
||||
notmuch-hello-insert-alltags)
|
||||
notmuch-saved-searches
|
||||
'((:name "inbox" :query "tag:inbox not tag:trash" :key "i")
|
||||
(:name "flagged" :query "tag:flagged" :key "f")
|
||||
(:name "sent" :query "tag:sent" :key "s")
|
||||
(:name "drafts" :query "tag:draft" :key "d"))
|
||||
notmuch-archive-tags '("-inbox" "-unread"))
|
||||
|
||||
;; (setq-hook! 'notmuch-show-mode-hook line-spacing 0)
|
||||
|
||||
(add-to-list 'doom-real-buffer-functions #'notmuch-interesting-buffer nil #'eq)
|
||||
|
||||
(advice-add #'notmuch-start-notmuch-sentinel :around #'+notmuch*dont-confirm-on-kill-process)
|
||||
|
||||
;; Visual enhancements
|
||||
(defun +notmuch|center-window ()
|
||||
(setq-local visual-fill-column-width 90)
|
||||
(visual-fill-column-mode))
|
||||
(add-hook 'notmuch-show-mode-hook #'+notmuch|center-window)
|
||||
|
||||
;; modeline doesn't have much use in these modes
|
||||
(add-hook! (notmuch-show-mode notmuch-tree-mode notmuch-search-mode)
|
||||
#'hide-mode-line-mode))
|
||||
|
||||
|
||||
(def-package! org-mime
|
||||
:after (org notmuch)
|
||||
:config (setq org-mime-library 'mml))
|
||||
|
||||
|
||||
(def-package! counsel-notmuch
|
||||
:when (featurep! :completion ivy)
|
||||
:commands counsel-notmuch
|
||||
:after notmuch)
|
||||
|
||||
(def-package! helm-notmuch
|
||||
:when (featurep! :completion helm)
|
||||
:commands helm-notmuch
|
||||
:after notmuch)
|
||||
|
9
modules/email/notmuch/packages.el
Normal file
9
modules/email/notmuch/packages.el
Normal file
|
@ -0,0 +1,9 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; email/notmuch/packages.el
|
||||
|
||||
(package! notmuch)
|
||||
(package! org-mime)
|
||||
(when (featurep! :completion ivy)
|
||||
(package! counsel-notmuch))
|
||||
(when (featurep! :completion helm)
|
||||
(package! helm-notmuch))
|
4
modules/email/wanderlust/autoload.el
Normal file
4
modules/email/wanderlust/autoload.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;;; email/wanderlust/autoload.el -*- lexical-binding: t; -*-
|
||||
|
||||
;;;###autoload
|
||||
(defalias '=wanderlust #'wl)
|
78
modules/email/wanderlust/config.el
Normal file
78
modules/email/wanderlust/config.el
Normal file
|
@ -0,0 +1,78 @@
|
|||
;;; app/wanderlust/config.el -*- lexical-binding: t; -*-
|
||||
|
||||
(def-package! wl
|
||||
:defer t
|
||||
:config
|
||||
(setq mail-user-agent 'wl-user-agent
|
||||
pgg-scheme 'gpg
|
||||
mime-edit-split-message nil)
|
||||
|
||||
(when (fboundp 'define-mail-user-agent)
|
||||
(define-mail-user-agent
|
||||
'wl-user-agent
|
||||
'wl-user-agent-compose
|
||||
'wl-draft-send
|
||||
'wl-draft-kill
|
||||
'mail-send-hook))
|
||||
|
||||
(setq wl-init-file (expand-file-name "wl.el" doom-private-dir))
|
||||
|
||||
(setq wl-demo nil
|
||||
wl-interactive-exit t
|
||||
wl-interactive-send t
|
||||
wl-stay-folder-window t
|
||||
wl-folders-file (expand-file-name "folders.wl" doom-private-dir)
|
||||
wl-x-face-file (expand-file-name "xface" doom-private-dir)
|
||||
wl-draft-folder "%INBOX.drafts"
|
||||
wl-fcc "%INBOX.Sent")
|
||||
|
||||
(setq wl-message-truncate-lines t
|
||||
wl-summary-width 120
|
||||
wl-from (format "%s <%s>" user-full-name user-mail-address)
|
||||
;; wl-organization "Secret Conspiracy"
|
||||
wl-local-domain (cadr (split-string user-mail-address "@"))
|
||||
wl-message-ignored-field-list
|
||||
'(".*Received:"
|
||||
".*Path:"
|
||||
".*Id:"
|
||||
"^References:"
|
||||
"^Replied:"
|
||||
"^Errors-To:"
|
||||
"^Lines:"
|
||||
"^Sender:"
|
||||
".*Host:"
|
||||
"^Xref:"
|
||||
"^Content-Type:"
|
||||
"^Precedence:"
|
||||
"^Status:"
|
||||
"^X.*:"
|
||||
"^MIME.*:"
|
||||
"^In-Reply-To:"
|
||||
"^Content-Transfer-Encoding:"
|
||||
"^List-.*:"))
|
||||
|
||||
(setq wl-message-visible-field-list '("^Message-Id:" "^User-Agent:" "^X-Mailer:" "^X-Face:"))
|
||||
|
||||
(when (featurep! +gmail)
|
||||
(setq elmo-imap4-default-server "imap.gmail.com"
|
||||
elmo-imap4-default-port 993
|
||||
elmo-imap4-default-authenticate-type 'clear ; CRAM-MD5
|
||||
elmo-imap4-default-user ""
|
||||
elmo-imap4-default-stream-type 'ssl
|
||||
elmo-imap4-set-seen-flag-explicitly t)
|
||||
|
||||
(setq wl-smtp-connection-type 'starttls
|
||||
wl-smtp-posting-port 587
|
||||
wl-smtp-authenticate-type "plain"
|
||||
wl-smtp-posting-user ""
|
||||
wl-smtp-posting-server "smtp.gmail.com"))
|
||||
|
||||
(setq wl-message-id-domain wl-local-domain)
|
||||
|
||||
(when (featurep! :editor evil)
|
||||
;; Neither wl-folder-mode or wl-summary-mode are correctly defined as major
|
||||
;; modes, so `evil-set-initial-state' won't work here.
|
||||
(add-hook! '(wl-folder-mode-hook wl-summary-mode)
|
||||
#'evil-emacs-state))
|
||||
|
||||
(add-hook 'mime-edit-mode-hook #'auto-fill-mode))
|
4
modules/email/wanderlust/packages.el
Normal file
4
modules/email/wanderlust/packages.el
Normal file
|
@ -0,0 +1,4 @@
|
|||
;; -*- no-byte-compile: t; -*-
|
||||
;;; app/wanderlust/packages.el
|
||||
|
||||
(package! wanderlust)
|
Loading…
Add table
Add a link
Reference in a new issue