[PATCH v2 1/2] emacs: Add thread-outline functionality
authorDaniel Schoepe <daniel@schoepe.org>
Sat, 17 Dec 2011 00:16:33 +0000 (01:16 +0100)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:40:57 +0000 (09:40 -0800)
d5/bfadb6045f7446b71bc7b7e2635c6f78e933b4 [new file with mode: 0644]

diff --git a/d5/bfadb6045f7446b71bc7b7e2635c6f78e933b4 b/d5/bfadb6045f7446b71bc7b7e2635c6f78e933b4
new file mode 100644 (file)
index 0000000..b4b51c2
--- /dev/null
@@ -0,0 +1,347 @@
+Return-Path: <daniel@schoepe.org>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id AC827429E21\r
+       for <notmuch@notmuchmail.org>; Fri, 16 Dec 2011 16:16:44 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.8\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.8 tagged_above=-999 required=5\r
+       tests=[DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1,\r
+       RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id ZMjumJamEOYQ for <notmuch@notmuchmail.org>;\r
+       Fri, 16 Dec 2011 16:16:43 -0800 (PST)\r
+Received: from mail-ee0-f53.google.com (mail-ee0-f53.google.com\r
+ [74.125.83.53])       (using TLSv1 with cipher RC4-SHA (128/128 bits))        (No client\r
+ certificate requested)        by olra.theworths.org (Postfix) with ESMTPS id\r
+ 42355431FB6   for <notmuch@notmuchmail.org>; Fri, 16 Dec 2011 16:16:43 -0800\r
+ (PST)\r
+Received: by mail-ee0-f53.google.com with SMTP id d41so4339652eek.26\r
+       for <notmuch@notmuchmail.org>; Fri, 16 Dec 2011 16:16:42 -0800 (PST)\r
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=schoepe.org; s=google;\r
+       h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references;\r
+       bh=6WLk3lB0KgzEPW/HdNJ7zzRC/n8KncJo6WJZbcWxzRQ=;\r
+       b=GtL22qpiG1RlOUXZWmz3pHFNp55phNifwqjcwmS2HRNCQKTNB59fVjIfor5UO/30X9\r
+       uQNUOY5tDF54D3wXWwCKqoCH53MFfDmw32Pgz8PvpYcHOJvsUK2tIgodYSqr4SLG2Wey\r
+       lyg0lVkLxAs4xZG/py0tBlSWotEmdli8qGcNQ=\r
+Received: by 10.14.3.167 with SMTP id 39mr328477eeh.6.1324081002702;\r
+       Fri, 16 Dec 2011 16:16:42 -0800 (PST)\r
+Received: from localhost (dslb-088-069-153-158.pools.arcor-ip.net.\r
+       [88.69.153.158])\r
+       by mx.google.com with ESMTPS id z54sm3932898eeh.5.2011.12.16.16.16.40\r
+       (version=TLSv1/SSLv3 cipher=OTHER);\r
+       Fri, 16 Dec 2011 16:16:41 -0800 (PST)\r
+From: Daniel Schoepe <daniel@schoepe.org>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v2 1/2] emacs: Add thread-outline functionality\r
+Date: Sat, 17 Dec 2011 01:16:33 +0100\r
+Message-Id: <1324080994-18146-2-git-send-email-daniel@schoepe.org>\r
+X-Mailer: git-send-email 1.7.7.3\r
+In-Reply-To: <1324080994-18146-1-git-send-email-daniel@schoepe.org>\r
+References: <1307921480-17130-1-git-send-email-daniel.schoepe@googlemail.com>\r
+       <1324080994-18146-1-git-send-email-daniel@schoepe.org>\r
+Cc: Daniel Schoepe <daniel.schoepe@googlemail.com>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sat, 17 Dec 2011 00:16:44 -0000\r
+\r
+From: Daniel Schoepe <daniel.schoepe@googlemail.com>\r
+\r
+This patch adds some functionality to display the outline for threads\r
+displayed by notmuch-show.  The entries in the outline buffer are\r
+links to the corresponding message in the notmuch-show buffer.\r
+---\r
+ emacs/notmuch-lib.el  |   12 +++\r
+ emacs/notmuch-show.el |  195 ++++++++++++++++++++++++++++++++++++++++++++++++-\r
+ 2 files changed, 206 insertions(+), 1 deletions(-)\r
+\r
+diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el\r
+index 0f856bf..a8be8b1 100644\r
+--- a/emacs/notmuch-lib.el\r
++++ b/emacs/notmuch-lib.el\r
+@@ -43,6 +43,10 @@\r
+ (defvar notmuch-folders nil\r
+   "Deprecated name for what is now known as `notmuch-saved-searches'.")\r
\r
++(defvar notmuch-show-outline-buffer nil\r
++  "Outline buffer associated with a notmuch-show buffer.")\r
++(make-variable-buffer-local 'notmuch-show-outline-buffer)\r
++\r
+ (defun notmuch-saved-searches ()\r
+   "Common function for querying the notmuch-saved-searches variable.\r
\r
+@@ -91,9 +95,17 @@ the user hasn't set this variable with the old or new value."\r
+   "Return the user.other_email value (as a list) from the notmuch configuration."\r
+   (split-string (notmuch-config-get "user.other_email") "\n"))\r
\r
++(declare-function notmuch-show-outline-buffer-name  "notmuch-show" (&optional buf))\r
++\r
+ (defun notmuch-kill-this-buffer ()\r
+   "Kill the current buffer."\r
+   (interactive)\r
++  ;; if we are in a notmuch-show buffer, kill the associated outline buffer, if any\r
++  (when (eq major-mode 'notmuch-show-mode)\r
++    (let ((outline-buf notmuch-show-outline-buffer))\r
++      (when outline-buf\r
++      (mapc #'delete-window (get-buffer-window-list outline-buf))\r
++      (kill-buffer outline-buf))))\r
+   (kill-buffer (current-buffer)))\r
\r
+ ;;\r
+diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el\r
+index 63b01e5..e7ce811 100644\r
+--- a/emacs/notmuch-show.el\r
++++ b/emacs/notmuch-show.el\r
+@@ -107,6 +107,57 @@ indentation."\r
+   :group 'notmuch\r
+   :type 'boolean)\r
\r
++(defcustom notmuch-always-show-outline nil\r
++  "Always open an outline buffer when viewing a thread?"\r
++  :group 'notmuch\r
++  :type 'boolean)\r
++\r
++(defcustom notmuch-outline-format\r
++  (list "%a - %r")\r
++  "Format used for thread-outline lines.\r
++\r
++This is a list supporting the following types of elements:\r
++For a symbol, its value is used if non-nil.\r
++A string is inserted verbatim with the exception\r
++ of the following %-constructs:\r
++ %a - Author\r
++ %d - Date\r
++ %s - Subject\r
++ %r - Relative date\r
++For a list of the form `(:eval FORM)', form is evaluated\r
++ and its result displayed.\r
++\r
++The variables author, subject, date and reldate will be bound to\r
++their respective values when this is interpreted, and can be\r
++used in (:eval ..)-elements or directly as symbols."\r
++  :group 'notmuch\r
++  :type\r
++  '(repeat (choice (const :tag "Author" author)\r
++                 (const :tag "Date" date)\r
++                 (const :tag "Relative date" reldate)\r
++                 (string :tag "Format string")\r
++                 (list :tag "Custom expression (will be evaluated when rendering)"\r
++                       (const :tag "" :eval)\r
++                       sexp))))\r
++\r
++(defface notmuch-outline '((t :inherit default))\r
++  "Face used to display (unhighlighted) lines in thread outlines"\r
++  :group 'notmuch)\r
++\r
++(defface notmuch-outline-highlighted\r
++  '((((class color) (background light)) (:background "#f0f0f0"))\r
++    (((class color) (background dark)) (:background "#303030")))\r
++  "Face used to display highlight the current message in the outline buffer"\r
++  :group 'notmuch)\r
++\r
++(defvar notmuch-outline-mode-map\r
++  (let ((map (make-sparse-keymap)))\r
++    (define-key map "n" 'next-line)\r
++    (define-key map "p" 'previous-line)\r
++    (define-key map "q" 'kill-buffer-and-window)\r
++    (define-key map "x" 'kill-buffer-and-window)\r
++    map))\r
++\r
+ (defmacro with-current-notmuch-show-message (&rest body)\r
+   "Evaluate body with current buffer set to the text of current message"\r
+   `(save-excursion\r
+@@ -747,12 +798,27 @@ current buffer, if possible."\r
+     ;; message.\r
+     (put-text-property message-start message-end :notmuch-message-extent (cons message-start message-end))\r
\r
++    ;; Save the indentation depth, used by `notmuch-show-outline'\r
++    (put-text-property message-start message-end :notmuch-depth depth)\r
++\r
+     (let ((headers-overlay (make-overlay headers-start headers-end))\r
+           (invis-specs (list headers-invis-spec message-invis-spec)))\r
+       (overlay-put headers-overlay 'invisible invis-specs)\r
+       (overlay-put headers-overlay 'priority 10))\r
+     (overlay-put (make-overlay body-start body-end) 'invisible message-invis-spec)\r
\r
++    ;; Add callbacks that update the outline buffer when moving between messages.\r
++    ;; Due to the mindbogglingly absurd semantics of point-entered and point-left\r
++    ;; this function will will be run up to _four_ times when moving between messages:\r
++    (let ((goto-msg-func\r
++         `(lambda (before after)\r
++            (if (and (>= after (marker-position ,message-start))\r
++                     (< after (marker-position ,message-end)))\r
++                (notmuch-outline-highlight-message ,message-start)))))\r
++      (add-text-properties message-start message-end\r
++                         (list 'point-entered goto-msg-func\r
++                               'point-left goto-msg-func)))\r
++\r
+     ;; Save the properties for this message. Currently this saves the\r
+     ;; entire message (augmented it with other stuff), which seems\r
+     ;; like overkill. We might save a reduced subset (for example, not\r
+@@ -808,6 +874,130 @@ a corresponding notmuch search."\r
+                       'help-echo "Mouse-1, RET: search for this message"\r
+                       'face goto-address-mail-face))))\r
\r
++(defun notmuch-show-message-is-visible ()\r
++  "Return t if current message is visible."\r
++  (plist-get (notmuch-show-get-message-properties) :message-visible))\r
++\r
++(defun notmuch-outline-render-format (format)\r
++  "Render FORMAT, as described in `notmuch-outline-format'"\r
++  (let ((author (notmuch-show-get-from))\r
++      (date (notmuch-show-get-date))\r
++      (subject (notmuch-show-get-subject))\r
++      (reldate (plist-get (notmuch-show-get-message-properties)\r
++                          :date_relative)))\r
++    (mapconcat (lambda (elem)\r
++               (cond\r
++                ((symbolp elem) (or (symbol-value elem) ""))\r
++                ((stringp elem)\r
++                 (let ((str elem))\r
++                   (mapc (lambda (subst)\r
++                           (setq str\r
++                                 (replace-regexp-in-string (car subst)\r
++                                                           (cdr subst)\r
++                                                           str)))\r
++                         `(("%a" . ,author)\r
++                           ("%s" . ,subject)\r
++                           ("%d" . ,date)\r
++                           ("%r" . ,reldate)))\r
++                   str))\r
++                ((and (listp elem) (eq (car elem) :eval))\r
++                 (eval (second elem)))\r
++                (t (error "Unknown element in `notmuch-outline-format': %S" elem))))\r
++             format\r
++             "")))\r
++\r
++(defun notmuch-outline-highlight-message (msg-start)\r
++  "Highlight message starting at MSG-START.\r
++\r
++The highlighting will take place in the outline buffer, while\r
++MSG-START refers to a position in the corresponding notmuch-show buffer."\r
++  (when (buffer-live-p notmuch-show-outline-buffer)\r
++    (with-current-buffer notmuch-show-outline-buffer\r
++      (remove-overlays nil nil 'current-message t)\r
++      (save-excursion\r
++      (goto-char (point-min))\r
++      (while (and (not (equal (get-text-property (point) :message-start)\r
++                          msg-start))\r
++                (not (eobp)))\r
++        (forward-line))\r
++      (unless (eobp)\r
++        (let ((ovl\r
++               (make-overlay (line-beginning-position)\r
++                             (line-end-position))))\r
++        (overlay-put ovl 'face 'notmuch-outline-highlighted)\r
++        (overlay-put ovl 'current-message t)))))))\r
++\r
++(defun notmuch-show-create-outline-buffer (&optional buf)\r
++  "Create an outline buffer for show-buffer BUF.\r
++\r
++Returns the created buffer."\r
++\r
++  (generate-new-buffer (concat (buffer-name buf) " - outline")))\r
++\r
++(defun notmuch-outline-message ()\r
++  "Outline the message under the point.\r
++\r
++Expects the point to be on the beginning of the first line of the message."\r
++  (lexical-let*\r
++      ((msg-start (car (notmuch-show-message-extent)))\r
++       (outline-buf notmuch-show-outline-buffer)\r
++       (goto-message\r
++      (lambda (btn)\r
++        (let ((win (get-buffer-window outline-buf)))\r
++          (when win\r
++            (select-window (get-buffer-window outline-buf))\r
++            (when (marker-buffer msg-start)\r
++              (switch-to-buffer-other-window (marker-buffer msg-start))\r
++              (notmuch-outline-highlight-message msg-start)\r
++              (goto-char (marker-position msg-start))\r
++              (when (not (notmuch-show-message-is-visible))\r
++                (notmuch-show-toggle-message))))))))\r
++    (let ((indentation (or (get-text-property (point) :notmuch-depth) 0))\r
++        (button-label (notmuch-outline-render-format\r
++                       notmuch-outline-format)))\r
++      (with-current-buffer outline-buf\r
++      (indent-to indentation)\r
++      (insert button-label)\r
++      (make-text-button (line-beginning-position) (line-end-position)\r
++                        'action goto-message\r
++                        'follow-link t\r
++                        'help-echo "mouse-1, RET: show this message"\r
++                        'face 'notmuch-outline)\r
++      (put-text-property (line-beginning-position) (line-end-position)\r
++                         :message-start msg-start)\r
++      (insert "\n")))))\r
++\r
++(defun notmuch-show-outline ()\r
++  "Generate an outline for the current buffer.\r
++\r
++This function must only be called in a notmuch-show buffer."\r
++  (interactive)\r
++  (if (buffer-live-p notmuch-show-outline-buffer)\r
++      (switch-to-buffer-other-window notmuch-show-outline-buffer)\r
++    (let ((outline-buf (notmuch-show-create-outline-buffer))\r
++        (inhibit-point-motion-hooks t))\r
++      (setq notmuch-show-outline-buffer outline-buf)\r
++      (save-excursion\r
++      (with-current-buffer outline-buf\r
++        (notmuch-outline-mode))\r
++      (goto-char (point-min))\r
++      (while (not (eobp))\r
++        (notmuch-outline-message)\r
++        (goto-char (marker-position (cdr (notmuch-show-message-extent)))))\r
++      (with-current-buffer outline-buf\r
++        (setq buffer-read-only t)))\r
++      (notmuch-outline-highlight-message (car (notmuch-show-message-extent)))\r
++      (let ((win (selected-window)))\r
++      (switch-to-buffer-other-window outline-buf)\r
++      (select-window win)))))\r
++\r
++(defun notmuch-outline-mode ()\r
++  (interactive)\r
++  (kill-all-local-variables)\r
++  (use-local-map notmuch-outline-mode-map)\r
++  (setq major-mode 'notmuch-show-outline-mode\r
++      mode-name "notmuch-show-outline"))\r
++\r
+ ;;;###autoload\r
+ (defun notmuch-show (thread-id &optional parent-buffer query-context buffer-name crypto-switch)\r
+   "Run \"notmuch show\" with the given thread ID and display results.\r
+@@ -881,7 +1071,9 @@ buffer."\r
+     ;; Set the header line to the subject of the first open message.\r
+     (setq header-line-format (notmuch-show-strip-re (notmuch-show-get-subject)))\r
\r
+-    (notmuch-show-mark-read)))\r
++    (notmuch-show-mark-read)\r
++    (when notmuch-always-show-outline\r
++      (notmuch-show-outline))))\r
\r
+ (defun notmuch-show-refresh-view (&optional crypto-switch)\r
+   "Refresh the current view (with crypto switch if prefix given).\r
+@@ -941,6 +1133,7 @@ thread id.  If a prefix is given, crypto processing is toggled."\r
+       (define-key map "P" 'notmuch-show-previous-message)\r
+       (define-key map "n" 'notmuch-show-next-open-message)\r
+       (define-key map "p" 'notmuch-show-previous-open-message)\r
++      (define-key map "o" 'notmuch-show-outline)\r
+       (define-key map (kbd "DEL") 'notmuch-show-rewind)\r
+       (define-key map " " 'notmuch-show-advance-and-archive)\r
+       (define-key map (kbd "M-RET") 'notmuch-show-open-or-close-all)\r
+-- \r
+1.7.7.3\r
+\r