[PATCH 2/3] emacs: Add thread-outline functionality
Daniel Schoepe
daniel.schoepe at googlemail.com
Sun Jun 12 16:31:19 PDT 2011
This patch adds some functionality to display the outline for threads
displayed by notmuch-show. The entries in the outline buffer are
links to the corresponding message in the notmuch-show buffer.
---
emacs/notmuch-lib.el | 7 +++
emacs/notmuch-show.el | 144 ++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 150 insertions(+), 1 deletions(-)
diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
index a21dc14..6918218 100644
--- a/emacs/notmuch-lib.el
+++ b/emacs/notmuch-lib.el
@@ -91,9 +91,16 @@ the user hasn't set this variable with the old or new value."
"Return the user.primary_email value (as a list) from the notmuch configuration."
(split-string (notmuch-config-get "user.other_email") "\n"))
+(declare-function notmuch-show-outline-buffer-name "notmuch-show" (&optional buf))
+
(defun notmuch-kill-this-buffer ()
"Kill the current buffer."
(interactive)
+ ;; if we are in a notmuch-show buffer, kill the associated outline buffer, if any
+ (when (eq major-mode 'notmuch-show-mode)
+ (let ((outline-buf (get-buffer (notmuch-show-outline-buffer-name))))
+ (when outline-buf
+ (kill-buffer outline-buf))))
(kill-buffer (current-buffer)))
;;
diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
index aecd35f..4f2a30e 100644
--- a/emacs/notmuch-show.el
+++ b/emacs/notmuch-show.el
@@ -107,6 +107,48 @@ same as that of the previous message."
:group 'notmuch
:type 'boolean)
+(defcustom notmuch-always-show-outline nil
+ "Should an outline of the thread always be opened?"
+ :group 'notmuch
+ :type 'boolean)
+
+(defcustom notmuch-outline-format
+ '(("author" . "%s")
+ "-"
+ ("reldate" . "%s"))
+ "Format in which thread-outline entries are displayed
+
+The following fields are supported: date, reldate, author,
+subject. The list can also contain strings as elements which
+will be printed literally. This variable can also be a function
+that will be given the message as returned by
+`notmuch-show-get-message-properties' and should return a
+string."
+ :group 'notmuch
+ :type '(repeat (choice (string :tag "string")
+ (cons (choice (const :tag "author" "author")
+ (const :tag "subject" "subject")
+ (const :tag "date" "date")
+ (const :tag "reldate" "reldate"))
+ (string :tag "format specifier")))))
+
+(defface notmuch-outline '((t :inherit default))
+ "Face used to display (unhighlighted) lines in thread outlines"
+ :group 'notmuch)
+
+(defvar notmuch-outline-mode-map
+ (let ((map (make-sparse-keymap)))
+ (define-key map "n" 'next-line)
+ (define-key map "p" 'previous-line)
+ (define-key map "q" 'notmuch-kill-this-buffer)
+ map))
+
+(defvar notmuch-outline-button-map
+ (let ((map (copy-keymap button-map)))
+ (define-key map (kbd "<mouse-1>") 'push-button)
+ map)
+ "Keymap used for buttons in thread outlines.")
+
(defmacro with-current-notmuch-show-message (&rest body)
"Evaluate body with current buffer set to the text of current message"
`(save-excursion
@@ -787,6 +829,103 @@ current buffer, if possible."
(defvar notmuch-show-parent-buffer nil)
(make-variable-buffer-local 'notmuch-show-parent-buffer)
+(defun notmuch-goto-marker (m)
+"Open corresponding buffer and go to marker position in another window."
+ (switch-to-buffer-other-window (marker-buffer m))
+ (goto-char (marker-position m)))
+
+(defun notmuch-show-message-is-visible ()
+ "Return t if current message is visible."
+ (plist-get (notmuch-show-get-message-properties) :message-visible))
+
+(defun notmuch-outline-render-format (format)
+ "Render FORMAT, interpreted as described for `notmuch-outline-format'"
+ (if (functionp format)
+ (funcall format (notmuch-show-get-message-properties))
+ (mapconcat
+ (lambda (entry)
+ (if (consp entry)
+ (let ((key (car entry))
+ (fmt (cdr entry)))
+ (cond
+ ((equal key "author") (format fmt (notmuch-show-get-from)))
+ ((equal key "date") (format fmt (notmuch-show-get-date)))
+ ((equal key "subject") (format fmt (notmuch-show-get-subject)))
+ ((equal key "reldate")
+ (format fmt (plist-get (notmuch-show-get-message-properties)
+ :date_relative)))
+ (t (concat "Unknown field: " (car entry)))))
+ entry))
+ format
+ " ")))
+
+(defun notmuch-show-outline-buffer-name (&optional buf)
+ "Return the name of the outline buffer for BUF."
+ (concat (buffer-name buf) " - outline"))
+
+(defun notmuch-show-has-outline ()
+ "Returns non-nil if there is an outline for the current thread."
+ (get-buffer (notmuch-show-outline-buffer-name)))
+
+(defun notmuch-outline-message ()
+ "Outline the message under the point.
+
+Expects the point to be on the beginning of the first line of the message."
+ (lexical-let* ((extent (notmuch-show-message-extent))
+ (buffer-name (notmuch-show-outline-buffer-name))
+ (goto-message (lambda (btn)
+ (select-window (get-buffer-window buffer-name))
+ (when (marker-buffer (car extent))
+ (notmuch-goto-marker (car extent))
+ (when (not (notmuch-show-message-is-visible))
+ (notmuch-show-toggle-message))))))
+ (let ((indentation 0)
+ (button-label (notmuch-outline-render-format
+ notmuch-outline-format)))
+ ;; this is not very robust if the output of notmuch-show changes
+ (while (string-equal (thing-at-point 'char) " ")
+ (incf indentation)
+ (forward-char))
+ (loop for i from 1 to indentation do
+ (princ " ")) ;; somewhat ugly
+ (princ button-label)
+ (with-current-buffer standard-output
+ (make-button (line-beginning-position) (line-end-position)
+ 'action goto-message
+ 'keymap notmuch-outline-button-map
+ 'face 'notmuch-outline)
+ (put-text-property (line-beginning-position) (line-end-position)
+ :message-start (car extent)))
+ (princ "\n"))))
+
+(defun notmuch-show-outline ()
+ "Generate an outline for the current buffer.
+
+This function must only be called in a notmuch-show buffer."
+ (interactive)
+ (let ((buf-name (notmuch-show-outline-buffer-name)))
+ ;; In the extremly rare case that the user might have been doing
+ ;; work in a buffer with the exact same name of the outline buffer
+ ;; we don't want to kill that buffer
+ (kill-buffer-if-not-modified buf-name)
+ (save-excursion
+ (with-output-to-temp-buffer buf-name
+ (with-current-buffer buf-name
+ (notmuch-outline-mode))
+ (goto-char (point-min))
+ (while (not (eobp))
+ (notmuch-outline-message)
+ (goto-char (marker-position (cdr (notmuch-show-message-extent)))))
+ (with-current-buffer buf-name
+ (setq buffer-read-only t))))))
+
+(defun notmuch-outline-mode ()
+ (interactive)
+ (kill-all-local-variables)
+ (use-local-map notmuch-outline-mode-map)
+ (setq major-mode 'notmuch-show-outline-mode
+ mode-name "notmuch-show-outline"))
+
;;;###autoload
(defun notmuch-show (thread-id &optional parent-buffer query-context buffer-name crypto-switch)
"Run \"notmuch show\" with the given thread ID and display results.
@@ -846,7 +985,9 @@ function is used. "
;; Set the header line to the subject of the first open message.
(setq header-line-format (notmuch-show-strip-re (notmuch-show-get-subject)))
- (notmuch-show-mark-read)))
+ (notmuch-show-mark-read)
+ (when notmuch-always-show-outline
+ (notmuch-show-outline))))
(defvar notmuch-show-stash-map
(let ((map (make-sparse-keymap)))
@@ -888,6 +1029,7 @@ function is used. "
(define-key map "P" 'notmuch-show-previous-message)
(define-key map "n" 'notmuch-show-next-open-message)
(define-key map "p" 'notmuch-show-previous-open-message)
+ (define-key map "o" 'notmuch-show-outline)
(define-key map (kbd "DEL") 'notmuch-show-rewind)
(define-key map " " 'notmuch-show-advance-and-archive)
(define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all)
--
1.7.5.4
More information about the notmuch
mailing list