* WIP: use org-mode for writing rich-text messages

This commit is contained in:
djcb
2012-05-23 17:04:26 +03:00
parent ecf18e9ac9
commit a493ffdb1d
2 changed files with 210 additions and 25 deletions

View File

@ -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,16 +423,20 @@ 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
(setq message-default-charset 'utf-8))
@ -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
@ -520,23 +538,9 @@ Gnus' `message-mode'."
(unless (eq compose-type 'edit)
(when message-signature
(message-insert-signature)))
;; 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)
@ -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)
(provide 'mu4e-compose)

View File

@ -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