* WIP: use org-mode for writing rich-text messages
This commit is contained in:
@ -237,7 +237,7 @@ separator is never written to file. Also see
|
||||
;; search for the first empty line
|
||||
(if (search-forward-regexp (concat "^$"))
|
||||
(replace-match
|
||||
(concat (propertize mail-header-separator 'read-only t 'intangible t)))
|
||||
(concat (propertize mail-header-separator 'intangible t)))
|
||||
;; no empty line? then append one
|
||||
(progn
|
||||
(goto-char (point-max))
|
||||
@ -423,15 +423,19 @@ needed, set the Fcc header, and register the handler function."
|
||||
;; update the file on disk -- ie., without the separator
|
||||
(mu4e~proc-add (buffer-file-name) mu4e-drafts-folder)) nil t))
|
||||
|
||||
(defconst mu4e~compose-hidden-headers
|
||||
`("^References:" "^Face:" "^X-Face:"
|
||||
"^X-Draft-From:" "^User-agent:")
|
||||
"Hidden headers when composing.")
|
||||
|
||||
|
||||
(define-derived-mode mu4e-compose-mode message-mode "mu4e:compose"
|
||||
"Major mode for the mu4e message composition, derived from `message-mode'.
|
||||
\\{message-mode-map}."
|
||||
(let ((message-hidden-headers
|
||||
`("^References:" "^Face:" "^X-Face:" "^X-Draft-From:" "^User-agent:")))
|
||||
(let ((message-hidden-headers mu4e~compose-hidden-headers))
|
||||
(use-local-map mu4e-compose-mode-map)
|
||||
(message-hide-headers)
|
||||
(make-local-variable 'message-default-charset)
|
||||
(message-hide-headers)
|
||||
|
||||
;; if the default charset is not set, use UTF-8
|
||||
(unless message-default-charset
|
||||
@ -443,7 +447,21 @@ needed, set the Fcc header, and register the handler function."
|
||||
;; set the default directory to the user's home dir; this is probably more
|
||||
;; useful e.g. when finding an attachment file the directory the current
|
||||
;; mail files lives in...
|
||||
(setq default-directory (expand-file-name "~/"))))
|
||||
(setq default-directory (expand-file-name "~/"))
|
||||
|
||||
;; setup the fcc-stuff, if needed
|
||||
(add-hook 'message-send-hook
|
||||
(lambda ()
|
||||
;; for safety, always save the draft before sending
|
||||
(set-buffer-modified-p t)
|
||||
(save-buffer)
|
||||
(mu4e~setup-fcc-maybe)) nil t)
|
||||
|
||||
;; when the message has been sent.
|
||||
(add-hook 'message-sent-hook
|
||||
(lambda ()
|
||||
(setq mu4e-sent-func 'mu4e-sent-handler)
|
||||
(mu4e~proc-sent (buffer-file-name) mu4e-drafts-folder)) nil)))
|
||||
|
||||
|
||||
(defconst mu4e~compose-buffer-max-name-length 30
|
||||
@ -524,20 +542,6 @@ Gnus' `message-mode'."
|
||||
;; set compose mode -- so now hooks can run
|
||||
(mu4e-compose-mode)
|
||||
|
||||
;; setup the fcc-stuff, if needed
|
||||
(add-hook 'message-send-hook
|
||||
(lambda ()
|
||||
;; for safety, always save the draft before sending
|
||||
(set-buffer-modified-p t)
|
||||
(save-buffer)
|
||||
(mu4e~setup-fcc-maybe)) nil t)
|
||||
|
||||
;; when the message has been sent.
|
||||
(add-hook 'message-sent-hook
|
||||
(lambda ()
|
||||
(setq mu4e-sent-func 'mu4e-sent-handler)
|
||||
(mu4e~proc-sent (buffer-file-name) mu4e-drafts-folder)) nil t))
|
||||
|
||||
;; buffer is not user-modified yet
|
||||
(mu4e~compose-set-friendly-buffer-name compose-type)
|
||||
(set-buffer-modified-p nil)
|
||||
@ -545,8 +549,7 @@ Gnus' `message-mode'."
|
||||
;; now jump to some use positions, and start writing that mail!
|
||||
(if (member compose-type '(new forward))
|
||||
(message-goto-to)
|
||||
(message-goto-body)))
|
||||
|
||||
(message-goto-body))))
|
||||
|
||||
(defun mu4e-sent-handler (docid path)
|
||||
"Handler function, called with DOCID and PATH for the just-sent
|
||||
@ -695,5 +698,8 @@ message."
|
||||
'message-kill-buffer
|
||||
'message-send-hook)
|
||||
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(provide 'mu4e-compose)
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
;;; org-mu4e -- Support for links to mu4e messages/queries from within org-mode
|
||||
;;; 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 Dirk-Jan C. Binnema
|
||||
|
||||
@ -71,6 +72,184 @@ the query (for paths starting with 'query:')."
|
||||
(t (message "mu4e: unrecognized link type '%s'" path))))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
;;; 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
|
||||
(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."
|
||||
(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)))
|
||||
(body (org-export-string raw-body 'org (file-name-directory tmp-file)))
|
||||
;; 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 raw-body 'html (file-name-directory tmp-file))
|
||||
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
|
||||
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 (where)
|
||||
"If WHERE is the symbol 'body, prefix headers with '#' block;
|
||||
when it is 'headers, remove this."
|
||||
(save-excursion
|
||||
(message-narrow-to-headers)
|
||||
(goto-char (point-min))
|
||||
(case where
|
||||
(headers
|
||||
(when (looking-at "#")
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^#" nil t)
|
||||
(replace-match ""))))
|
||||
(body
|
||||
(unless (looking-at "#")
|
||||
(goto-char (point-min))
|
||||
(while (re-search-forward "^" nil t)
|
||||
(replace-match "#")))))
|
||||
(widen)))
|
||||
|
||||
(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."
|
||||
(message "Converting to html")
|
||||
(when org-mu4e-convert-to-html
|
||||
(org~mu4e-mime-convert-to-html)))
|
||||
|
||||
|
||||
(defun org~mu4e-mime-switch-headers-or-body (&optional firsttime)
|
||||
"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)))
|
||||
(where ;; is point in the headers or in the body?
|
||||
(if (> (point) sepapoint) 'body 'headers))
|
||||
(first-char (buffer-substring (point-min) (1+ (point-min))))
|
||||
(state-changed
|
||||
(if (string= first-char "#")
|
||||
(eq where 'headers)
|
||||
(eq where 'body))))
|
||||
(when (or firsttime state-changed)
|
||||
;; now make sure the headers correspond to the mode...
|
||||
(case where
|
||||
(headers
|
||||
(org~mu4e-mime-decorate-headers 'headers)
|
||||
(mu4e-compose-mode)
|
||||
(add-hook 'message-send-hook
|
||||
'org~mu4e-mime-convert-to-html-maybe t t))
|
||||
(body
|
||||
(org-mode)
|
||||
(org~mu4e-mime-decorate-headers 'body)
|
||||
(local-set-key (kbd "M-m")
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(org~mu4e-mime-decorate-headers 'headers)
|
||||
(mu4e-compose-mode)
|
||||
(setq org-mu4e-compose-org-mode t)
|
||||
(add-hook 'message-send-hook
|
||||
'org~mu4e-mime-convert-to-html-maybe t t))))
|
||||
(otherwise (error "unexpected where %S" where)))
|
||||
(setq org-mu4e-compose-org-mode t)
|
||||
;; and add the hook
|
||||
(add-hook 'post-command-hook
|
||||
'org~mu4e-mime-switch-headers-or-body t t))))
|
||||
|
||||
(defvar org-mu4e-compose-org-mode nil)
|
||||
(setq org-mu4e-compose-org-mode nil)
|
||||
|
||||
(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))
|
||||
(error "Need org-mode or mu4e-compose-mode"))
|
||||
(if (not org-mu4e-compose-org-mode)
|
||||
(progn
|
||||
(org~mu4e-mime-switch-headers-or-body t)
|
||||
(message "org-mu4e-compose-org-mode enabled"))
|
||||
(progn ;; otherwise, remove crap
|
||||
(remove-hook 'post-command-hook 'org~mu4e-mime-switch-headers-or-body t)
|
||||
(org~mu4e-mime-decorate-headers 'headers) ;; shut off org-mode stuff
|
||||
(setq org-mu4e-compose-org-mode nil)
|
||||
(mu4e-compose-mode)
|
||||
(message "org-mu4e-compose-org-mode disabled"))))
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
(provide 'org-mu4e)
|
||||
;;; org-mu4e.el ends here
|
||||
|
||||
Reference in New Issue
Block a user