Skip to content

Latest commit

 

History

History
621 lines (549 loc) · 25 KB

starter-kit-mu4e.org

File metadata and controls

621 lines (549 loc) · 25 KB

Starter Kit mu4e configuration

This is part of the Emacs Starter Kit.

Starter kit mailer

Preamble

(if (executable-find "mu")
    (progn

mu4e configuration

;; (add-to-list 'load-path "/usr/local/share/emacs/site-lisp/mu4e")

(require 'mu4e)
(require 'smtpmail)

;;(setq mu4e-maildir "/home/garrido/.Maildir")

;; set mu4e as default email client
(setq mail-user-agent 'mu4e-user-agent)

;; allow for updating mail using 'U' in the main view:
(when (executable-find "myofflineimap")
  (setq mu4e-get-mail-command "myofflineimap"))

;; .offlineimaprc is a unix conf file
(add-to-list 'auto-mode-alist '("\\.offlineimaprc" . conf-mode))

;; don't save message to Sent Messages, Gmail/IMAP takes care of this
;; (setq mu4e-sent-messages-behavior 'delete)

;; save attachment to my desktop (this can also be a function)
(setq mu4e-attachment-dir "/tmp/")

;; attempt to show images when viewing messages
(setq mu4e-view-show-images t)

(setq mu4e-confirm-quit nil
      mu4e-headers-date-format "%d-%m-%Y %H:%M")

;; don't keep message buffers around
(setq message-kill-buffer-on-exit t)

;; Check for mail every 3 minutes
(setq mu4e-update-interval (* 3 60))

;; disable minibuffer message
(setq mu4e-hide-index-messages t)

;; always show mail adress
(setq mu4e-view-show-addresses t)

;; remove duplicate message in header view
(setq mu4e-headers-skip-duplicates t)

;; try to render html mail
;;(setq mu4e-view-prefer-html t)
;; (when (executable-find "html2text")
;;   (setq mu4e-html2text-command "html2text -utf8 -nobs -width 72"))
(require 'mu4e-contrib)
(setq mu4e-html2text-command 'mu4e-shr2text)

(add-hook 'mu4e-view-mode-hook
          (lambda()
            ;; try to emulate some of the eww key-bindings
            (local-set-key (kbd "<tab>") 'shr-next-link)
            (local-set-key (kbd "<backtab>") 'shr-previous-link)))

(add-to-list 'mu4e-view-actions
             '("ViewInBrowser" . mu4e-action-view-in-browser) t)

;; split vertically when reading mail
(setq mu4e-split-view 'vertical)

;; header fields
(setq mu4e-headers-fields
      '( (:date       . 18)
         (:flags      . 4)
         (:from-or-to . 22)
         (:subject    . nil)))

;; (setq mu4e-view-use-gnus t)

;; (setq
;;  mu4e-index-cleanup nil      ;; don't do a full cleanup check
;;  mu4e-index-lazy-check t)    ;; don't consider up-to-date dirs

(setq doc-view-cache-directory "/tmp/docview")

Better fancy chars

Basically change nothing except uparrow and downarrow (flags are hidden)

(defconst mu4e-headers-from-or-to-prefix '("" . ""))
(setq mu4e-use-fancy-chars t)
(setq mu4e-headers-has-child-prefix    '("+"  . "+"))
(setq mu4e-headers-empty-parent-prefix '("-"  . "-"))
(setq mu4e-headers-first-child-prefix  '("\\" . "\\"))
(setq mu4e-headers-duplicate-prefix    '("="  . "="))
(setq mu4e-headers-default-prefix      '("|"  . "|"))
(setq mu4e-headers-unread-mark         '("u"  . ""))
(setq mu4e-headers-attach-mark         '("a"  . "+"))
(setq mu4e-headers-flagged-mark        '("F"  . "!"))
(setq mu4e-headers-new-mark            '("N"  . "*"))
(setq mu4e-headers-passed-mark         '("P"  . "»"))
(setq mu4e-headers-replied-mark        '("R"  . "«"))
(setq mu4e-headers-seen-mark           '("S"  . ""))
(setq mu4e-headers-trashed-mark        '("T"  . ""))
(setq mu4e-headers-signed-mark         '("s" . ""))

(setq mu4e-headers-thread-child-prefix '("├>" . ""))
(setq mu4e-headers-thread-last-child-prefix '("└>" . ""))
(setq mu4e-headers-thread-connection-prefix '("" . " "))
(setq mu4e-headers-thread-orphan-prefix '("┬>" . ""))
(setq mu4e-headers-thread-single-orphan-prefix '("─>" . ""))
(setq mu4e-headers-threaded-label   '("T" . ""))
(setq mu4e-headers-full-label       '("F" . ""))
(setq mu4e-headers-related-label    '("R" . ""))
(setq mu4e-headers-list-mark      '("s" . ""))
(setq mu4e-headers-personal-mark  '("p" . ""))


(setq mu4e-headers-unread-mark    '("u" . "📩 "))
(setq mu4e-headers-draft-mark     '("D" . "🚧 "))
(setq mu4e-headers-flagged-mark   '("F" . "🚩 "))
(setq mu4e-headers-new-mark       '("N" . "📩 "))
(setq mu4e-headers-passed-mark    '("P" . ""))
(setq mu4e-headers-replied-mark   '("R" . ""))
(setq mu4e-headers-seen-mark      '("S" . "📩 "))
(setq mu4e-headers-trashed-mark   '("T" . "🗑️"))
(setq mu4e-headers-attach-mark    '("a" . "📎 "))
(setq mu4e-headers-encrypted-mark '("x" . "🔑 "))

;; (setq
;;  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" . "⎕")
;;  mu4e-headers-list-mark      '("s" . "🔈")
;;  mu4e-headers-personal-mark  '("p" . "👨")
;;  mu4e-headers-calendar-mark  '("c" . "📅"))

Select and insert contact with ido

From http://emacs.stackexchange.com/questions/4209/using-ido-or-helm-to-auto-complete-email-addresses-in-mu4e

(defun sk-select-and-insert-contact (&optional start)
  (interactive)
  (let ((mail-abbrev-mode-regexp mu4e~compose-address-fields-regexp)
        (eoh ;; end-of-headers
         (save-excursion
           (goto-char (point-min))
           (search-forward-regexp mail-header-separator nil t))))
    (when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p))
      (let* ((end (point))
             (start
              (or start
                  (save-excursion
                    (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
                    (goto-char (match-end 0))
                    (point))))
             (contact
              (ido-completing-read "Contact: "
                                   mu4e~contacts-for-completion
                                   nil
                                   nil
                                   (buffer-substring-no-properties start end))))
        (unless (equal contact "")
          (kill-region start end)
          (insert contact))))))

org setting

Storing link to mails

;;store org-mode links to messages
;; (require 'org-mu4e)

;;store link to message if in header view, not to header query
(setq org-mu4e-link-query-in-headers-mode nil)

;; when mail is sent, automatically convert org body to HTML
(setq org-mu4e-convert-to-html t)

(define-key mu4e-headers-mode-map (kbd "C-c l") 'org-store-link)
(add-hook 'message-mode-hook
          (lambda ()
            (yas-minor-mode t)
            (auto-complete-mode t)
            (set-input-method "TeX")))

Htmlization with org-mime

(require 'org-mime)
(defun org-mime-org-buffer-htmlize ()
  "Create an email buffer containing the current org-mode file
      exported to html and encoded in both html and in org formats as
      mime alternatives."
  (interactive)
  (org-mime-send-buffer 'html)
  (message-goto-to))

(defun org-mime-subtree ()
  "Create an email buffer containing the current org-mode subtree
  exported to a org format or to the format specified by the
  MAIL_FMT property of the subtree."
  (interactive)
  (org-mime-send-subtree
   (or (org-entry-get nil "MAIL_FMT" org-mime-use-property-inheritance) 'org))
  (message-goto-to))

(defun htmlize-and-send ()
  "When in an org-mu4e-compose-org-mode message, htmlize and send it."
  (interactive)
  (when (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)
    (org-mime-htmlize)
    (message-send-and-exit)))

(defun org-mime-compose (body fmt file &optional to subject headers)
  (require 'message)
  (let ((bhook
         (lambda (body fmt)
           (let ((hook (intern (concat "org-mime-pre-"
                                       (symbol-name fmt)
                                       "-hook"))))
             (if (> (eval `(length ,hook)) 0)
                 (with-temp-buffer
                   (insert body)
                   (goto-char (point-min))
                   (eval `(run-hooks ',hook))
                   (buffer-string))
               body))))
        (fmt (if (symbolp fmt) fmt (intern fmt)))
        (files (org-element-map (org-element-parse-buffer) 'link
                 (lambda (link)
                   (when (string= (org-element-property :type link) "file")
                     (file-truename (org-element-property :path link)))))))
    (compose-mail to subject headers nil)
    (message-goto-body)
    (cond
     ((eq fmt 'org)
      (require 'ox-org)
      (insert (org-export-string-as
               (org-babel-trim (funcall bhook body 'org)) 'org t)))
     ((eq fmt 'ascii)
      (require 'ox-ascii)
      (insert (org-export-string-as
               (concat "#+Title:\n" (funcall bhook body 'ascii)) 'ascii t)))
     ((or (eq fmt 'html) (eq fmt 'html-ascii))
      (require 'ox-ascii)
      (require 'ox-org)
      (let* ((org-link-file-path-type 'absolute)
             ;; we probably don't want to export a huge style file
             (org-export-htmlize-output-type 'inline-css)
             (org-html-with-latex 'dvisvgm)
             (html-and-images
              (org-mime-replace-images
               (org-export-string-as (funcall bhook body 'html) 'html t)))
             (images (cdr html-and-images))
             (html (org-mime-apply-html-hook (car html-and-images))))
        (insert (org-mime-multipart
                 (org-export-string-as
                  (org-babel-trim
                   (funcall bhook body (if (eq fmt 'html) 'org 'ascii)))
                  (if (eq fmt 'html) 'org 'ascii) t)
                 html)
                (mapconcat 'identity images "\n")))))
    (mapc #'mml-attach-file files)))

(add-hook 'org-ctrl-c-ctrl-c-hook 'htmlize-and-send t)
;; (define-key mu4e-compose-mode-map (kbd "C-c o") 'org-mu4e-compose-org-mode)
(global-set-key (kbd "C-c o") 'org-mu4e-compose-org-mode)

Better mail completion

                                        ;need this for hash access
(require 'subr-x)

(defun bjm/read-contact-list ()
  "Return a list of email addresses"
  (with-temp-buffer
    (split-string (buffer-string) "\n" t)))

;; code from https://github.com/abo-abo/swiper/issues/596
(defun bjm/counsel-email-action (contact)
  (with-ivy-window
    (insert contact)))

;; bind comma to launch new search
(defvar bjm/counsel-email-map
  (let ((map (make-sparse-keymap)))
    (define-key map "," 'bjm/counsel-email-more)
    map))

(defun bjm/counsel-email-more ()
  "Insert email address and prompt for another."
  (interactive)
  (ivy-call)
  (with-ivy-window
    (insert ", "))
  (delete-minibuffer-contents)
  (setq ivy-text ""))

;; ivy contacts
;; based on http://kitchingroup.cheme.cmu.edu/blog/2015/03/14/A-helm-mu4e-contact-selector/
(defun bjm/ivy-select-and-insert-contact (&optional start)
  (interactive)
  ;; make sure mu4e contacts list is updated - I was having
  ;; intermittent problems that this was empty but couldn't see why
  (mu4e~request-contacts-maybe)
  (let ((eoh ;; end-of-headers
         (save-excursion
           (goto-char (point-min))
           (search-forward-regexp mail-header-separator nil t)))
        ;; append full sorted contacts list to favourites and delete duplicates
        (contacts-list
         (delq nil (delete-dups (mu4e~sort-contacts-for-completion (hash-table-keys mu4e~contacts))))))

    ;; only run if we are in the headers section
    (when (and eoh (> eoh (point)) (mail-abbrev-in-expansion-header-p))
      (let* ((end (point))
             (start
              (or start
                  (save-excursion
                    (re-search-backward "\\(\\`\\|[\n:,]\\)[ \t]*")
                    (goto-char (match-end 0))
                    (point))))
             (initial-input (buffer-substring-no-properties start end)))

        (kill-region start end)

        (ivy-read "Contact: "
                  contacts-list
                  :re-builder #'ivy--regex
                  :sort nil
                  :initial-input initial-input
                  :action 'bjm/counsel-email-action
                  :keymap bjm/counsel-email-map)
        ))))

;;launch automatically
(add-hook 'mu4e-compose-mode-hook 'bjm/ivy-select-and-insert-contact)

;;ivy contacts for use anywhere
;;based on http://kitchingroup.cheme.cmu.edu/blog/2015/03/14/A-helm-mu4e-contact-selector/
(defun bjm/ivy-select-and-insert-contact-anywhere ()
  (interactive)
  (let (contacts-list contact)
    ;;append full sorted contacts list to favourites and delete duplicates
    (setq contacts-list
          (delq nil (delete-dups (mu4e~sort-contacts-for-completion (hash-table-keys mu4e~contacts)))))
    (setq contact
          (ivy-read "Contact: "
                    contacts-list
                    :re-builder #'ivy--regex
                    :sort nil))
    (unless (equal contact "")
      (insert contact))))

Remove maildir string in mode-line

(defun mu4e~headers-jump-to-maildir (maildir)
  "Show the messages in maildir (user is prompted to ask what
maildir)."
  (interactive
   (let ((maildir (mu4e-ask-maildir "Jump to maildir: ")))
     (list maildir)))
  (when maildir
    (mu4e-mark-handle-when-leaving)
    (mu4e-headers-search
     (format "%s" maildir))))

Save all attachments

;;; mu4e-view-save-all-attachments.el -- Save all attachments from view mode.
;;; Stephen J Eglen 2021


;; I've created this based on the work of Phil Jackson that required
;; an older version of mu4e.  This version requires the GNUS article
;; code for reading mu4e messages.
;; https://gist.github.com/philjackson/aecfab1706f05079aec7000e328fd183

(defvar bulk-saved-attachments-dir (expand-file-name "/tmp"))

(defun cleanse-subject (sub)
  (replace-regexp-in-string
   "[^A-Z0-9]+"
   "-"
   (downcase sub)))

(defun mu4e-view-save-all-attachments (&optional arg)
  "Save all MIME parts from current mu4e gnus view buffer."
  ;; Copied from mu4e-view-save-attachments
  (interactive "P")
  (cl-assert (and (eq major-mode 'mu4e-view-mode)
                  (derived-mode-p 'gnus-article-mode)))
  (let* ((msg (mu4e-message-at-point))
         (id (cleanse-subject (mu4e-message-field msg :subject)))
         (attachdir (concat bulk-saved-attachments-dir "/" id))
         (parts (mu4e~view-gather-mime-parts))
         (handles '())
         (files '())
         dir)
    (mkdir attachdir t)
    (dolist (part parts)
      (let ((fname (or 
                    (cdr (assoc 'filename (assoc "attachment" (cdr part))))
                    (seq-find #'stringp
                              (mapcar (lambda (item) (cdr (assoc 'name item)))
                                      (seq-filter 'listp (cdr part)))))))
        (when fname
          (push `(,fname . ,(cdr part)) handles)
          (push fname files))))
    (if files
        (progn
          (setq dir
                (if arg (read-directory-name "Save to directory: ")
                  attachdir))
          (cl-loop for (f . h) in handles
                   when (member f files)
                   do (mm-save-part-to-file h (expand-file-name f dir))))
      (mu4e-message "No attached files found"))))

Accounts

Setting accounts

(defvar sk-mu4e-account-alist
  '(("LAL"
     (user-mail-address  "xavier.garrido@ijclab.in2p3.fr")
     (user-full-name     "Xavier Garrido")
     (mu4e-drafts-folder "/LAL/drafts")
     (mu4e-sent-folder   "/LAL/sent")
     (mu4e-compose-signature (concat
                              "  GARRIDO Xavier       Laboratoire de l'Accélérateur Linéaire\n"
                              "  NEMO                 Université Paris-Sud 11               \n"
                              "  garrido@lal.in2p3.fr UMR 8607                              \n"
                              "  garrido@in2p3.fr     Batiment 200                          \n"
                              "  +33 1.64.46.84.28    91898 Orsay Cedex, France             \n"
                              ))))
  ("Gmail"
   (user-mail-address  "xavier.garrido@gmail.com")
   (user-full-name     "Xavier Garrido")
   (mu4e-drafts-folder "/Gmail/drafts")
   (mu4e-sent-folder   "/Gmail/sent")
   (mu4e-compose-signature (concat
                            "Xavier Garrido\n"
                            "http://xgarrido.github.io\n"))))

(setq mu4e-user-mail-address-list
      (mapcar (lambda (account) (cadr (assq 'user-mail-address account)))
              sk-mu4e-account-alist))

Set account when composing mail

(defun sk-mu4e-set-account ()
  "Set the account for composing a message."
  (let* ((account
          (if mu4e-compose-parent-message
              (let ((maildir (mu4e-message-field mu4e-compose-parent-message :maildir)))
                (string-match "/\\(.*?\\)/" maildir)
                (match-string 1 maildir))
            (completing-read (format "Compose with account: (%s) "
                                     (mapconcat #'(lambda (var) (car var))
                                                sk-mu4e-account-alist "/"))
                             (mapcar #'(lambda (var) (car var)) sk-mu4e-account-alist)
                             nil t nil nil (caar sk-mu4e-account-alist))))
         (account-vars (cdr (assoc account sk-mu4e-account-alist))))
    (if account-vars
        (mapc #'(lambda (var)
                  (set (car var) (cadr var)))
              account-vars)
      (error "No email account found"))))

;; ask for account when composing mail
;; (add-hook 'mu4e-compose-pre-hook 'sk-mu4e-set-account)

Gmail configuration

(defun sk-email-gmail ()
  (setq message-send-mail-function 'smtpmail-send-it)
  (setq smtpmail-stream-type 'starttls)
  (setq smtpmail-default-smtp-server "smtp.gmail.com")
  (setq smtpmail-smtp-server "smtp.gmail.com")
  (setq smtpmail-smtp-service 587)
  (setq mu4e-drafts-folder "/Gmail/drafts")
  (setq mu4e-sent-folder   "/Gmail/sent")
  (setq mu4e-trash-folder  "/Gmail/trash")
  (setq mu4e-refile-folder (lambda (msg)
                             (cond
                              ;; messages to the mu mailing list go to the /org-mode folder
                              ((or (mu4e-message-contact-field-matches msg :cc "emacs-orgmode@gnu.org")
                                   (mu4e-message-contact-field-matches msg :to "emacs-orgmode@gnu.org"))
                               "/Gmail/ml/org-mode")
                              ((mu4e-message-contact-field-matches msg :from "notifications@github.com")
                               "/Gmail/ml/github")
                              ;; everything else goes to /archive
                              ;; important to have a catch-all at the end!
                              (t  "/Gmail/archive"))))
  (setq mu4e-maildir-shortcuts  '(("/Gmail/inbox"   . ?i)
                                  ("/Gmail/sent"    . ?s)
                                  ("/Gmail/trash"   . ?t)
                                  ("/Gmail/archive" . ?a)))
  )

LAL configuration

(defun sk-email-lal ()
  (setq user-mail-address  "xavier.garrido@ijclab.in2p3.fr")
  (setq user-full-name     "Xavier Garrido")
  (setq message-send-mail-function 'smtpmail-send-it)
  (setq smtpmail-stream-type 'ssl)
  (setq smtpmail-default-smtp-server "zrelay.in2p3.fr")
  (setq smtpmail-smtp-server "zrelay.in2p3.fr")
  (setq smtpmail-smtp-service 465)
  (setq mu4e-compose-signature (concat
                                "  GARRIDO Xavier       IJCLab                    \n"
                                "  CMB                  Université Paris-Saclay   \n"
                                "  garrido@lal.in2p3.fr Batiment 200              \n"
                                "  +33 1.64.46.84.28    91898 Orsay Cedex, France \n"
                                ))
  (setq mu4e-drafts-folder "/LAL/drafts")
  (setq mu4e-sent-folder   "/LAL/sent")
  (setq mu4e-trash-folder  "/LAL/trash")
  (setq mu4e-refile-folder (lambda (msg)
                             (cond
                              ;; messages to the mu mailing list go to the /na61 folder
                              ;; ((or (mu4e-message-contact-field-matches msg :cc "na61-all@cern.ch")
                              ;;      (mu4e-message-contact-field-matches msg :to "na61-all@cern.ch"))
                              ;;  "/LAL/inbox/Experiment/NA61")
                              ;; everything else goes to /archive
                              ;; important to have a catch-all at the end!
                              (t (concat "/LAL/archives/" (format-time-string "%Y" (current-time)))))))
  ;; (setq mu4e-refile-folder "/LAL/archives/2016" )
  (setq mu4e-maildir-shortcuts  '(("/LAL/inbox"         . ?i)
                                  ("/LAL/sent"          . ?s)
                                  ("/LAL/trash"         . ?t)
                                  ("/LAL/archives/2024" . ?a)))
  )

Hydra

(defun sk-mu4e-lal ()
  (interactive)
  (sk-email-lal)
  (mu4e)
  (mu4e~headers-jump-to-maildir "/LAL/inbox"))
;; (defun sk-mu4e-gmail()
;;   (interactive)
;;   (sk-email-gmail)
;;   (mu4e)
;;   (mu4e~headers-jump-to-maildir "/Gmail/inbox"))

;; (key-chord-define-global
;;  "!!"
;;  (defhydra hydra-email (:color blue :hint nil)
;;    "
;;   [mu4e] _g_mail _l_al"
;;    ("g" sk-mu4e-gmail)
;;    ("l" sk-mu4e-lal)))

(key-chord-define-global "!!" 'sk-mu4e-lal)

org-msg

(require 'org-msg)
(setq org-msg-options "html-postamble:nil H:5 num:nil ^:{} toc:nil"
      org-msg-startup "hidestars indent inlineimages"
      org-msg-greeting-fmt "\nBonjour *%s*,\n\n"
      org-msg-greeting-name-limit 3
      org-msg-signature "
 #+begin_signature
  GARRIDO Xavier       Laboratoire de l'Accélérateur Linéaire\n
  NEMO                 Université Paris-Sud 11               \n
  garrido@lal.in2p3.fr UMR 8607                              \n
  garrido@in2p3.fr     Batiment 200                          \n
  +33 1.64.46.84.28    91898 Orsay Cedex, France             \n
 #+end_signature"
      )
(org-msg-mode)

Postamble

))