* add support for org-mode links to messages/queries

- org-mu4e.el: add "mu4e:"-link type and handlers to org-mode
  - Makefile.am: add org-mu4e.el to package
  - mu4e-hdrs.el: register handler functions for server data *outside* header
    mode, so they can be used independently (and we can link to messages even
    when mu4e is not active already)
  - mu4e-view.el, mu-server.1, mu-proc.el, mu-cmd-server.c: support viewing
    messages by msgid (rather than only by docid); document it
This commit is contained in:
djcb 2012-01-06 12:31:28 +02:00
parent e4d5d6de8a
commit 4dbb05f33c
7 changed files with 143 additions and 72 deletions

View File

@ -1,4 +1,4 @@
## Copyright (C) 2008-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ## Copyright (C) 2008-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
## ##
## This program is free software; you can redistribute it and/or modify ## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by ## it under the terms of the GNU General Public License as published by
@ -35,6 +35,7 @@ elisp_DATA= \
mu4e-view.el \ mu4e-view.el \
mu4e-proc.el \ mu4e-proc.el \
mu4e-send.el \ mu4e-send.el \
mu4e-version.el mu4e-version.el \
org-mu4e.el
EXTRA_DIST=$(elisp_DATA) EXTRA_DIST=$(elisp_DATA)

View File

@ -1,11 +1,9 @@
;; mu4e-hdrs.el -- part of mm, the mu mail user agent ;; mu4e-hdrs.el -- part of mu4e, the mu mail user agent
;; ;;
;; Copyright (C) 2011 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Keywords: email
;; Version: 0.0
;; This file is not part of GNU Emacs. ;; This file is not part of GNU Emacs.
;; ;;
@ -28,8 +26,6 @@
;; descriptions of emails, aka 'headers' (not to be confused with headers like ;; descriptions of emails, aka 'headers' (not to be confused with headers like
;; 'To:' or 'Subject:') ;; 'To:' or 'Subject:')
;; mm
;; Code: ;; Code:
(eval-when-compile (require 'cl)) (eval-when-compile (require 'cl))
@ -39,7 +35,6 @@
;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;; internal variables/constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defvar mu4e-last-expr nil (defvar mu4e-last-expr nil
"*internal* The most recent search expression.") "*internal* The most recent search expression.")
(defconst mu4e-hdrs-buffer-name "*mu4e-headers*" (defconst mu4e-hdrs-buffer-name "*mu4e-headers*"
"*internal* Name of the buffer for message headers.") "*internal* Name of the buffer for message headers.")
@ -322,6 +317,15 @@ after the end of the search results."
(fset 'mu4e-hdrs-mode-map mu4e-hdrs-mode-map) (fset 'mu4e-hdrs-mode-map mu4e-hdrs-mode-map)
;; we register our handler functions for the mu4e-proc (mu server) output
(setq mu4e-proc-error-func 'mu4e-hdrs-error-handler)
(setq mu4e-proc-update-func 'mu4e-hdrs-update-handler)
(setq mu4e-proc-header-func 'mu4e-hdrs-header-handler)
(setq mu4e-proc-found-func 'mu4e-hdrs-found-handler)
(setq mu4e-proc-view-func 'mu4e-hdrs-view-handler)
(setq mu4e-proc-remove-func 'mu4e-hdrs-remove-handler)
;; this last one is defined in mu4e-send.el
(setq mu4e-proc-compose-func 'mu4e-send-compose-handler)
(defun mu4e-hdrs-mode () (defun mu4e-hdrs-mode ()
"Major mode for displaying mua search results." "Major mode for displaying mua search results."
@ -336,16 +340,6 @@ after the end of the search results."
(make-local-variable 'mu4e-msg-map) (make-local-variable 'mu4e-msg-map)
(make-local-variable 'mu4e-thread-info-map) (make-local-variable 'mu4e-thread-info-map)
;; we register our handler functions for the mu4e-proc (mu server) output
(setq mu4e-proc-error-func 'mu4e-hdrs-error-handler)
(setq mu4e-proc-update-func 'mu4e-hdrs-update-handler)
(setq mu4e-proc-header-func 'mu4e-hdrs-header-handler)
(setq mu4e-proc-found-func 'mu4e-hdrs-found-handler)
(setq mu4e-proc-view-func 'mu4e-hdrs-view-handler)
(setq mu4e-proc-remove-func 'mu4e-hdrs-remove-handler)
;; this last one is defined in mu4e-send.el
(setq mu4e-proc-compose-func 'mu4e-send-compose-handler)
(setq (setq
mu4e-marks-map (make-hash-table :size 16 :rehash-size 2) mu4e-marks-map (make-hash-table :size 16 :rehash-size 2)
mu4e-msg-map (make-hash-table :size 1024 :rehash-size 2 :weakness nil) mu4e-msg-map (make-hash-table :size 1024 :rehash-size 2 :weakness nil)
@ -356,7 +350,7 @@ after the end of the search results."
buffer-undo-list t ;; don't record undo information buffer-undo-list t ;; don't record undo information
buffer-read-only t buffer-read-only t
overwrite-mode 'overwrite-mode-binary) overwrite-mode 'overwrite-mode-binary)
(setq header-line-format (setq header-line-format
(cons (cons
(make-string (floor (fringe-columns 'left t)) ?\s) (make-string (floor (fringe-columns 'left t)) ?\s)
@ -583,6 +577,7 @@ work well."
(unless docid (error "No message at point.")) (unless docid (error "No message at point."))
(mu4e-proc-view-msg docid))) (mu4e-proc-view-msg docid)))
(defun mu4e-hdrs-compose (compose-type) (defun mu4e-hdrs-compose (compose-type)
"Compose either a reply/forward based on the message at point. or "Compose either a reply/forward based on the message at point. or
start editing it. COMPOSE-TYPE is either `reply', `forward' or start editing it. COMPOSE-TYPE is either `reply', `forward' or
@ -619,12 +614,10 @@ start editing it. COMPOSE-TYPE is either `reply', `forward' or
(defun mu4e-ignore-marks () (defun mu4e-ignore-marks ()
"If there are still marks in the header list, warn the user." "If there are still marks in the header list, warn the user."
(if mu4e-marks-map (if mu4e-marks-map
(let* (let* ((num (hash-table-count mu4e-marks-map))
((num (unmark (or (= 0 num)
(hash-table-count mu4e-marks-map)) (y-or-n-p
(unmark (or (= 0 num) (format "Sure you want to unmark %d message(s)?" num)))))
(y-or-n-p
(format "Sure you want to unmark %d message(s)?" num)))))
(message nil) (message nil)
unmark)) unmark))
t) t)

View File

@ -1,11 +1,9 @@
;;; mu4e-proc.el -- part of mm, the mu mail user agent ;;; mu4e-proc.el -- part of mu4e, the mu mail user agent
;; ;;
;; Copyright (C) 2011 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Keywords: email
;; Version: 0.0
;; This file is not part of GNU Emacs. ;; This file is not part of GNU Emacs.
;; ;;
@ -418,10 +416,12 @@ set to e.g. '/drafts'; if this works, we will receive (:info :path
response." response."
(mu4e-proc-send-command "ping")) (mu4e-proc-send-command "ping"))
(defun mu4e-proc-view-msg (docid) (defun mu4e-proc-view-msg (docid-or-msgid)
"Get one particular message based on its DOCID. The result will "Get one particular message based on its DOCID-OR-MSGID. The result will
be delivered to the function registered as `mu4e-proc-message-func'." be delivered to the function registered as `mu4e-proc-message-func'."
(mu4e-proc-send-command "view %d" docid)) (if (stringp docid-or-msgid)
(mu4e-proc-send-command "view %s" docid-or-msgid)
(mu4e-proc-send-command "view %d" docid-or-msgid)))
(defun mu4e-proc-compose (compose-type docid) (defun mu4e-proc-compose (compose-type docid)
"Start composing a message with DOCID and COMPOSE-TYPE (a symbol, "Start composing a message with DOCID and COMPOSE-TYPE (a symbol,
@ -452,5 +452,6 @@ and update the database afterwards."
(with-current-buffer buf (with-current-buffer buf
(kill-buffer-and-window)))) (kill-buffer-and-window))))
(provide 'mu4e-proc) (provide 'mu4e-proc)
;; End of mu4e-proc.el

View File

@ -1,11 +1,9 @@
;; mu4e-view.el -- part of mu4e, the mu mail user agent ;; mu4e-view.el -- part of mu4e, the mu mail user agent
;; ;;
;; Copyright (C) 2011 Dirk-Jan C. Binnema ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema
;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Author: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ;; Maintainer: Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
;; Keywords: email
;; Version: 0.0
;; This file is not part of GNU Emacs. ;; This file is not part of GNU Emacs.
;; ;;
@ -49,6 +47,12 @@
(defvar mu4e-current-msg nil (defvar mu4e-current-msg nil
"*internal* The plist describing the current message.") "*internal* The plist describing the current message.")
(defun mu4e-view-message-with-msgid (msgid)
"View message with MSGID. This is meant for external programs
wanting to show specific messages - for example, `mu4e-org'."
(mu4e-proc-view-msg msgid))
(defun mu4e-view (msg hdrsbuf &optional update) (defun mu4e-view (msg hdrsbuf &optional update)
"Display the message MSG in a new buffer, and keep in sync with HDRSBUF. "Display the message MSG in a new buffer, and keep in sync with HDRSBUF.
'In sync' here means that moving to the next/previous message in 'In sync' here means that moving to the next/previous message in
@ -118,7 +122,7 @@ marking if it still had that."
(mu4e-mark-footer) (mu4e-mark-footer)
(mu4e-make-urls-clickable) (mu4e-make-urls-clickable)
(unless update (unless update
(mu4e-view-mark-as-read-maybe))))) (mu4e-view-mark-as-read-maybe)))))
@ -387,7 +391,7 @@ Seen; if the message is not New/Unread, do nothing."
(lexical-let ((url url)) (lexical-let ((url url))
(lambda () (lambda ()
(interactive) (interactive)
(browse-url url)))) (browse-url url))))
@ -411,7 +415,7 @@ number them so they can be opened using `mu4e-view-go-to-url'."
(replace-match (concat url (replace-match (concat url
(propertize (format "[%d]" num) (propertize (format "[%d]" num)
'face 'mu4e-view-url-number-face)))))))) 'face 'mu4e-view-url-number-face))))))))
;; raw mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; raw mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -506,37 +510,34 @@ number them so they can be opened using `mu4e-view-go-to-url'."
;; functions for org-contacts ;; functions for org-contacts
(defun mu4e-org-contacts-from (name-or-email) (defun mu4e-view-snarf-from (name-or-email)
"Get a message field if we are in view mode; NAME-OR-EMAIL should "Get the From:-data for the current message; NAME-OR-EMAIL should
be either 'name or 'email to get the corresponding field. If the be a symbol 'name or 'email to get the corresponding field. If the
field is not found, \"\" is returned. Use this with org-contact field is not found, \"\" is returned.
with a template like:
You can use this with e.g. org-contact with a template like:
(\"c\" \"Contacts\" entry (file \"~/Org/contacts.org\") (\"c\" \"Contacts\" entry (file \"~/Org/contacts.org\")
\"* %(mu4e-org-contacts-from 'name) \"* %(mu4e-view-snarf-from 'name)
:PROPERTIES: :PROPERTIES:
:EMAIL: %(mu4e-org-contacts-from 'email) :EMAIL: %(mu4e-view-snarf-from 'email)
:END:\"))) :END:\")))
See the `org-contacts' documentation for more details." See the `org-contacts' documentation for more details."
(with-current-buffer mu4e-view-buffer-name ;; hackish... (unless (eq major-mode 'mu4e-view-mode)
(unless (eq major-mode 'mu4e-view-mode) (error "Not in mu4e-view mode."))
(error "Not in mu4e-view mode.")) (unless mu4e-current-msg
(unless mu4e-current-msg (error "No current message."))
(error "No current message.")) (let ((from (car-safe (plist-get mu4e-current-msg :from))))
(let ((from (car-safe (plist-get mu4e-current-msg :from)))) (cond
(cond ((not from) "") ;; nothing found
((not from) "") ;; nothing found ((eq name-or-email 'name)
((eq name-or-email 'name) (or (car-safe from) ""))
(or (car-safe from) "")) ((eq name-or-email 'email)
((eq name-or-email 'email) (or (cdr-safe from) ""))
(or (cdr-safe from) "")) (t (error "Not supported: %S" name-or-email)))))
(t (error "Not supported: %S" name-or-email))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Interactive functions ;; Interactive functions

77
emacs/org-mu4e.el Normal file
View File

@ -0,0 +1,77 @@
;;; org-mu4e -- Support for links to mu4e messages/queries from within org-mode
;;
;; Copyright (C) 2012 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 the 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:
(require 'org)
(eval-when-compile (require 'cl))
(eval-when-compile (require 'mu4e))
(defun org-mu4e-store-link ()
"Store a link to a mu4e query or message."
(cond
;; storing links to queries
((eq major-mode 'mu4e-hdrs-mode)
(let* ((query mu4e-last-expr)
desc link)
(org-store-link-props :type "mu4e" :query query)
(setq
desc (org-make-link "mu4e:query:" query)
link desc)
(org-add-link-props :link link :description desc)
link))
;; storing links to messages
((eq major-mode 'mu4e-view-mode)
(let* ((msg mu4e-current-msg)
(msgid (or (plist-get msg :message-id) "<none>"))
(subject (or (plist-get msg :subject) "No subject"))
link)
(org-store-link-props :type "mu4e" :link link
:message-id msgid :subject subject)
(setq link (org-make-link "mu4e:msgid:" msgid))
(org-add-link-props :link link :description subject)
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:')."
(require 'mu4e)
(cond
((string-match "^msgid:\\(.+\\)" path)
(mu4e-view-message-with-msgid (match-string 1 path)))
((string-match "^query:\\(.+\\)" path)
(mu4e-search (match-string 1 path)))
(t (message "mu4e: unrecognized link type '%s'" path))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(provide 'org-mu4e)
;;; org-mu4e.el ends here

View File

@ -1,8 +1,8 @@
.TH MU-SERVER 1 "December 2011" "User Manuals" .TH MU-SERVER 1 "January 2012" "User Manuals"
.SH NAME .SH NAME
mu server \- the mu backend for the mm e-mail cleint mu server \- the mu backend for the mu4e e-mail cleint
.SH DESCRIPTION .SH DESCRIPTION
@ -131,7 +131,7 @@ Using the \fBview\fR command, we can all information (including the body) of a
particular e-mail message. particular e-mail message.
.nf .nf
-> view <docid> -> view <docid-or-msgid>
<- (:view <s-exp>) <- (:view <s-exp>)
.fi .fi

View File

@ -1,6 +1,6 @@
/* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/ /* -*-mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-*/
/* /*
** Copyright (C) 2010-2011 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl> ** Copyright (C) 2011-2012 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
** **
** This program is free software; you can redistribute it and/or modify it ** This program is free software; you can redistribute it and/or modify it
** under the terms of the GNU General Public License as published by the ** under the terms of the GNU General Public License as published by the
@ -441,8 +441,6 @@ get_docid_from_msgid (MuQuery *query, const char *str, GError **err)
} }
/* the string contains either a number (docid) or a message-id if it /* the string contains either a number (docid) or a message-id if it
* doesn't look like a number, and the query param is non-nil, try to * doesn't look like a number, and the query param is non-nil, try to
* locale the message with message-id in the database, and return its * locale the message with message-id in the database, and return its
@ -720,15 +718,15 @@ cmd_open (MuStore *store, GSList *args, GError **err)
static MuError static MuError
cmd_view (MuStore *store, GSList *args, GError **err) cmd_view (MuStore *store, MuQuery *query, GSList *args, GError **err)
{ {
MuMsg *msg; MuMsg *msg;
unsigned docid; unsigned docid;
char *sexp; char *sexp;
return_if_fail_param_num (args, 1, 1, "view <docid>"); return_if_fail_param_num (args, 1, 1, "view <docid>|<msgid>");
docid = get_docid (NULL, (const char*)args->data, err); docid = get_docid (query, (const char*)args->data, err);
if (docid == MU_STORE_INVALID_DOCID) if (docid == MU_STORE_INVALID_DOCID)
return server_error (err, MU_ERROR_IN_PARAMETERS, return server_error (err, MU_ERROR_IN_PARAMETERS,
"invalid docid"); "invalid docid");
@ -901,7 +899,7 @@ handle_command (Cmd cmd, MuStore *store, MuQuery *query, GSList *args,
case CMD_REMOVE: rv = cmd_remove (store, args, err); break; case CMD_REMOVE: rv = cmd_remove (store, args, err); break;
case CMD_SAVE: rv = cmd_save (store, args, err); break; case CMD_SAVE: rv = cmd_save (store, args, err); break;
case CMD_PING: rv = cmd_ping (store, args, err); break; case CMD_PING: rv = cmd_ping (store, args, err); break;
case CMD_VIEW: rv = cmd_view (store, args, err); break; case CMD_VIEW: rv = cmd_view (store, query, args, err); break;
case CMD_IGNORE: return TRUE; case CMD_IGNORE: return TRUE;
default: default: