diff options
Diffstat (limited to '_spacemacs.d/local/mu4e/org-mu4e.el')
-rw-r--r-- | _spacemacs.d/local/mu4e/org-mu4e.el | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/_spacemacs.d/local/mu4e/org-mu4e.el b/_spacemacs.d/local/mu4e/org-mu4e.el new file mode 100644 index 0000000..b9e9688 --- /dev/null +++ b/_spacemacs.d/local/mu4e/org-mu4e.el @@ -0,0 +1,323 @@ +;;; org-mu4e -- Support for links to mu4e messages/queries from within +;;; org-mode, and for writing message in org-mode, sending them as +;;; rich-text +;; +;; Copyright (C) 2012-2016 Dirk-Jan C. Binnema + +;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> +;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> +;; Keywords: outlines, hypermedia, calendar, mail +;; Version: 0.0 + +;; This file is not part of GNU Emacs. +;; +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of 1the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. + +;;; Commentary: + +;;; Code: + + +;; The expect version here is org 8.x + + +;; the 'noerror is just to make sure bytecompilations does not break... +;; FIXME: find a better solution +(require 'org nil 'noerror) +(require 'org-exp nil 'noerror) + +(eval-when-compile (require 'cl)) +(require 'mu4e) + +(defgroup org-mu4e nil + "Settings for the org-mode related functionality in mu4e." + :group 'mu4e + :group 'org) + +(defvar org-mu4e-link-query-in-headers-mode nil + "If non-nil, `org-store-link' in `mu4e-headers-mode' links to the +the current query; otherwise, it links to the message at point.") + +(defcustom org-mu4e-link-desc-func + (lambda (msg) (or (plist-get msg :subject) "No subject")) + "Function that takes a msg and returns a string for the +description part of an org-mode link. + +Example usage: + + (defun my-link-descr (msg) + (let ((subject (or (plist-get msg :subject) + \"No subject\")) + (date (or (format-time-string mu4e-headers-date-format + (mu4e-msg-field msg :date)) + \"No date\"))) + (concat subject \" \" date))) + + (setq org-mu4e-link-desc-func 'my-link-descr)" + :type 'function + :group 'org-mu4e) + +(defun org-mu4e-store-link () + "Store a link to a mu4e query or message." + (when (member major-mode '(mu4e-headers-mode mu4e-view-mode)) + (if (and (eq major-mode 'mu4e-headers-mode) + org-mu4e-link-query-in-headers-mode) + ;; storing links to queries + (let* ((query (mu4e-last-query)) + desc link) + (org-store-link-props :type "mu4e" :query query) + (setq + desc (concat "mu4e:query:" query) + link desc) + (org-add-link-props :link link :description desc) + link) + ;; storing links to messages + (let* ((msg (mu4e-message-at-point)) + (msgid (or (plist-get msg :message-id) "<none>")) + (from (or (plist-get msg :from) '(("none" . "none")))) + (fromname (car (car from))) + (fromaddress (cdr (car from))) + (to (or (plist-get msg :to) '(("none" . "none")))) + (toname (car (car to))) + (toaddress (cdr (car to))) + (fromto (if (mu4e-user-mail-address-p fromaddress) + (format "to %s <%s>" toname toaddress) + (format "from %s <%s>" fromname fromaddress))) + (date (plist-get msg :date)) + (date-ts (format-time-string (org-time-stamp-format t) date)) + (date-ts-ia (format-time-string (org-time-stamp-format t t) date)) + (subject (or (plist-get msg :subject) "<none>")) + link) + (org-store-link-props :type "mu4e" :link link + :message-id msgid) + (setq link (concat "mu4e:msgid:" msgid)) + (org-add-link-props :link link + :to (format "%s <%s>" toname toaddress) + :toname toname + :toaddress toaddress + :from (format "%s <%s>" fromname fromaddress) + :fromname fromname + :fromaddress fromaddress + :fromto fromto + :date date-ts-ia + :date-timestamp date-ts + :date-timestamp-inactive date-ts-ia + :subject subject + :description (funcall org-mu4e-link-desc-func msg)) + link)))) + +(org-add-link-type "mu4e" 'org-mu4e-open) +(add-hook 'org-store-link-functions 'org-mu4e-store-link) + +(defun org-mu4e-open (path) + "Open the mu4e message (for paths starting with 'msgid:') or run +the query (for paths starting with 'query:')." + (cond + ((string-match "^msgid:\\(.+\\)" path) + (mu4e-view-message-with-message-id (match-string 1 path))) + ((string-match "^query:\\(.+\\)" path) + (mu4e-headers-search (match-string 1 path) current-prefix-arg)) + (t (mu4e-error "mu4e: unrecognized link type '%s'" path)))) + +(defun org-mu4e-store-and-capture () + "Store a link to the current message or query (depending on +`org-mu4e-link-query-in-headers-mode', and capture it with +org-mode)." + (interactive) + (org-mu4e-store-link) + (org-capture)) + + + +;;; editing with org-mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; below, some functions for the org->html conversion +;; based on / inspired by Eric Schulte's org-mime.el +;; Homepage: http://orgmode.org/worg/org-contrib/org-mime.php +;; +;; EXPERIMENTAL +(defun org~mu4e-mime-file (ext path id) + "Create a file for an attachment." + (format (concat "<#part type=\"%s\" filename=\"%s\" " + "disposition=inline id=\"<%s>\">\n<#/part>\n") + ext path id)) + +(defun org~mu4e-mime-multipart (plain html &optional images) + "Create a multipart/alternative with text/plain and text/html alternatives. +If the html portion of the message includes images, wrap the html +and images in a multipart/related part." + (concat "<#multipart type=alternative><#part type=text/plain>" + plain + (when images "<#multipart type=related>") + "<#part type=text/html>" + html + images + (when images "<#/multipart>\n") + "<#/multipart>\n")) + +(defun org~mu4e-mime-replace-images (str current-file) + "Replace images in html files with cid links." + (let (html-images) + (cons + (replace-regexp-in-string ;; replace images in html + "src=\"\\([^\"]+\\)\"" + (lambda (text) + (format + "src=\"cid:%s\"" + (let* ((url (and (string-match "src=\"\\([^\"]+\\)\"" text) + (match-string 1 text))) + (path (expand-file-name + url (file-name-directory current-file))) + (ext (file-name-extension path)) + (id (replace-regexp-in-string "[\/\\\\]" "_" path))) + (add-to-list 'html-images + (org~mu4e-mime-file + (concat "image/" ext) path id)) + id))) + str) + html-images))) + +(defun org~mu4e-mime-convert-to-html () + "Convert the current body to html." + (unless (fboundp 'org-export-string-as) + (mu4e-error "require function 'org-export-string-as not found.")) + (unless (executable-find "dvipng") + (mu4e-error "Required program dvipng not found")) + (let* ((begin + (save-excursion + (goto-char (point-min)) + (search-forward mail-header-separator))) + (end (point-max)) + (raw-body (buffer-substring begin end)) + (tmp-file (make-temp-name (expand-file-name "mail" + temporary-file-directory))) + ;; because we probably don't want to skip part of our mail + (org-export-skip-text-before-1st-heading nil) + ;; because we probably don't want to export a huge style file + (org-export-htmlize-output-type 'inline-css) + ;; makes the replies with ">"s look nicer + (org-export-preserve-breaks t) + ;; dvipng for inline latex because MathJax doesn't work in mail + (org-export-with-LaTeX-fragments 'dvipng) + ;; to hold attachments for inline html images + (html-and-images + (org~mu4e-mime-replace-images + (org-export-string-as raw-body 'html t) + tmp-file)) + (html-images (cdr html-and-images)) + (html (car html-and-images))) + (delete-region begin end) + (save-excursion + (goto-char begin) + (newline) + (insert (org~mu4e-mime-multipart + raw-body html (mapconcat 'identity html-images "\n")))))) + +;; next some functions to make the org/mu4e-compose-mode switch as smooth as +;; possible. +(defun org~mu4e-mime-decorate-headers () + "Make the headers visually distinctive (org-mode)." + (save-excursion + (goto-char (point-min)) + (let* ((eoh (when (search-forward mail-header-separator) + (match-end 0))) + (olay (make-overlay (point-min) eoh))) + (when olay + (overlay-put olay 'face 'font-lock-comment-face))))) + +(defun org~mu4e-mime-undecorate-headers () + "Don't make the headers visually distinctive. +\(well, mu4e-compose-mode will take care of that)." + (save-excursion + (goto-char (point-min)) + (let* ((eoh (when (search-forward mail-header-separator) + (match-end 0)))) + (remove-overlays (point-min) eoh)))) + +(defvar org-mu4e-convert-to-html nil + "Whether to do automatic org-mode => html conversion when sending messages.") + +(defun org~mu4e-mime-convert-to-html-maybe () + "Convert to html if `org-mu4e-convert-to-html' is non-nil. +This function is called when sending a message (from +`message-send-hook') and, if non-nil, will send the message as +the rich-text version of the what is assumed to be an org-mode +body." + (when org-mu4e-convert-to-html + (mu4e-message "Converting to html") + (org~mu4e-mime-convert-to-html))) + +(defun org~mu4e-mime-switch-headers-or-body () + "Switch the buffer to either mu4e-compose-mode (when in headers) +or org-mode (when in the body)." + (interactive) + (let* ((sepapoint + (save-excursion + (goto-char (point-min)) + (search-forward-regexp mail-header-separator nil t)))) + ;; only do stuff when the sepapoint exist; note that after sending the + ;; message, this function maybe called on a message with the sepapoint + ;; stripped. This is why we don't use `message-point-in-header'. + (when sepapoint + (cond + ;; we're in the body, but in mu4e-compose-mode? + ;; if so, switch to org-mode + ((and (> (point) sepapoint) (eq major-mode 'mu4e-compose-mode)) + (org-mode) + (add-hook 'before-save-hook + (lambda () + (mu4e-error "Switch to mu4e-compose-mode (M-m) before saving.")) + nil t) + (org~mu4e-mime-decorate-headers) + (local-set-key (kbd "M-m") + (lambda (keyseq) + (interactive "kEnter mu4e-compose-mode key sequence: ") + (let ((func (lookup-key mu4e-compose-mode-map keyseq))) + (if func (funcall func) (insert keyseq)))))) + ;; we're in the headers, but in org-mode? + ;; if so, switch to mu4e-compose-mode + ((and (<= (point) sepapoint) (eq major-mode 'org-mode)) + (org~mu4e-mime-undecorate-headers) + (mu4e-compose-mode) + (add-hook 'message-send-hook 'org~mu4e-mime-convert-to-html-maybe nil t))) + ;; and add the hook + (add-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t t)))) + + +(defun org-mu4e-compose-org-mode () + "Pseudo-Minor mode for mu4e-compose-mode, to edit the message +body using org-mode." + (interactive) + (unless (member major-mode '(org-mode mu4e-compose-mode)) + (mu4e-error "Need org-mode or mu4e-compose-mode")) + ;; we can check if we're already in org-mu4e-compose-mode by checking if the + ;; post-command-hook is set; hackish...but a buffer-local variable does not + ;; seem to survive buffer switching + (if (not (member 'org~mu4e-mime-switch-headers-or-body post-command-hook)) + (progn + (org~mu4e-mime-switch-headers-or-body) + (mu4e-message + (concat + "org-mu4e-compose-org-mode enabled; " + "press M-m before issuing message-mode commands"))) + (progn ;; otherwise, remove crap + (remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t) + (org~mu4e-mime-undecorate-headers) ;; shut off org-mode stuff + (mu4e-compose-mode) + (message "org-mu4e-compose-org-mode disabled")))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +(provide 'org-mu4e) + +;;; org-mu4e.el ends here |