emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master 3e5c11a 1/4: Squashed 'packages/gnorb/' changes from 321b2


From: Eric Abrahamsen
Subject: [elpa] master 3e5c11a 1/4: Squashed 'packages/gnorb/' changes from 321b23b..4e7039a
Date: Thu, 23 Apr 2015 09:17:47 +0000

branch: master
commit 3e5c11a13981a1ff613cb4442ad644285c44e481
Author: Eric Abrahamsen <address@hidden>
Commit: Eric Abrahamsen <address@hidden>

    Squashed 'packages/gnorb/' changes from 321b23b..4e7039a
    
    4e7039a Various compiler-inspired improvements
    9b2b269 Use with-eval-after-load not eval-after-load
    86fa893 Fix up all cl-lib calls
    a59dac2 Use hook for determining Gnorb summary minor mode
    4d3de61 Various documentation improvements
    acb91c5 Fix doc error
    6fd368d Provide more format marks in summary buffers
    d9a1d89 Remove unused let variable
    3f9c534 Report Gnorb email tracking usage
    0f18c45 Allow persistent nnir search groups
    2d30b0c Reset window conf after nnir-run-gnorb
    160f43a New function for returning all tracked messages
    9efae5a Fix call to cl-subseq
    ce764a5 fixup with new quick reply command
    b0fe9ae New command `gnorb-gnus-quick-reply'
    5897188 Capture to child/sibling is done
    4f99dd7 Handle conditions where `registry-search' returns nil
    b951675 Merge capture-to-child branch
    13bb840 Hint which heading will be triggered
    c13f4df Better check for capture cleanup
    dfa0043 Safer usage of cl-subseq
    94fe1b8 Incorporate changes from Stefan M
    d2e1e11 Mention registry bugs in README
    a4089f8 Fix completing-read in message disassociation
    9c910c9 Re-raise errors in the triggering process
    648f5a7 Remove process mark after bulk association
    84ff7a7 Don't let attach errors derail the trigger process
    819b1e5 Suggest binding gnorb-org-view in Org Agenda
    9d64acb Update gnorb-registry-capture to use convenience funcs
    cc7d45b Be more careful handling org tags on BBDB records
    f585c03 condition-case the incoming trigger process
    821a6b2 Allow bulk association of messages
    4b19c83 New function for pruning dead associations
    09679fa Misspelled function name
    41c6778 nngnorb should be a virtual server
    6e6ee46 Zap another with-eval-after-load
    c3279d2 Fix tracking messages from virtual groups
    9220a10 Docstring fix
    c8b80c5 Bugfix for gnorb-gnus-view
    8c333ee Merge pull request #20 from totherme/master
    3801ad7 Check both gnus version and emacs version.
    94f6897 Don't use with-eval-after-load
    fd91084 Remove incorrect "fix" for Gnus 5.13
    8a9c167 Fix the cl-lib loading stuff
    
    git-subtree-dir: packages/gnorb
    git-subtree-split: 4e7039a15b47244e7bd2c580d8bce976a6116b5a
---
 README.org        |   29 ++++++-
 gnorb-bbdb.el     |   41 ++++-----
 gnorb-gnus.el     |  280 ++++++++++++++++++++++++++++++++++++-----------------
 gnorb-org.el      |   60 +++++++----
 gnorb-registry.el |  118 +++++++++++++++++++---
 gnorb-utils.el    |  169 ++++++++++++++++++++++----------
 gnorb.el          |   11 +-
 gnorb.info        |  199 +++++++++++++++++++++++++-------------
 gnorb.org         |  108 +++++++++++++++------
 gnorb.texi        |  132 +++++++++++++++++++------
 nngnorb.el        |   36 ++++---
 11 files changed, 834 insertions(+), 349 deletions(-)

diff --git a/README.org b/README.org
index 1f8f82f..9e2f9bd 100644
--- a/README.org
+++ b/README.org
@@ -16,7 +16,31 @@ mini mailboxes.
 *Note for previous users*: If you were using Gnorb from Github before
 it shifted to the Elpa repository, the email tracking mechanism has
 changed, please see the manual for details.
+** Known bugs/issues
+*** Gnus Registry
+Prior to late December, 2014, the Gnus registry had some issues with
+preserving "precious" entries while pruning.
 
+When the registry approaches its maximum size it will delete excess
+entries, a process referred to as "pruning". "Precious" entries are
+those that contain important information: they should not be pruned.
+
+Gnorb uses the registry to track associations between messages and Org
+headings, and marks those entries as precious. The entire process of
+tracking, in fact, relies on these entries being preserved, and Gnorb
+goes to some lengths to protect this information. Older versions of
+the registry could nevertheless delete those entries.
+
+These issues are fixed circa the end of December, 2014, around "Ma
+Gnus v0.12", whatever that means. If you think there's a possibility
+your registry is full, and associations are being deleted, you might
+consider upgrading to a recent Gnus.
+*** Multiple Associations
+Gnorb theoretically supports email messages being associated with
+multiple Org headings. In practice, however, this situation hasn't
+been thought through completely, and you may experience weirdness. If
+you do, and you have some ideas about how it should be handled, please
+contact the author and suggest them.
 ** Installation
 
 It's easiest to install Gnorb from Elpa: run `list-packages' and look
@@ -60,7 +84,10 @@ composing messages from... Or maybe it's just a case of NIH.
 Provide an Org Agenda command that does an email search for messages
 received in the visible date span, or day under point, etc. Make it
 work in the calendar, as well?
-*** TODO Capture to child/subtree trigger actions
+*** DONE Capture to child/subtree trigger actions
+:LOGBOOK:
+- State "DONE"       from "TODO"       [2015-03-17 Tue 17:42]
+:END:
 Add trigger actions that create new sibling or child headings on the
 original Org heading.
 *** TODO Gnus message tagging
diff --git a/gnorb-bbdb.el b/gnorb-bbdb.el
index 058011c..eb2f6eb 100644
--- a/gnorb-bbdb.el
+++ b/gnorb-bbdb.el
@@ -1,6 +1,6 @@
 ;;; gnorb-bbdb.el --- The BBDB-centric functions of gnorb
 
-;; Copyright (C) 2014  Eric Abrahamsen
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Author: Eric Abrahamsen <address@hidden>
 ;; Keywords: 
@@ -24,11 +24,9 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
-
-(require 'bbdb)
+(require 'bbdb nil t)
 (require 'gnorb-utils)
+(require 'cl-lib)
 
 (defgroup gnorb-bbdb nil
   "The BBDB bits of gnorb."
@@ -40,8 +38,9 @@
   :group 'gnorb-bbdb
   :type 'symbol)
 
-(unless (assoc gnorb-bbdb-org-tag-field bbdb-separator-alist)
-  (push `(,gnorb-bbdb-org-tag-field ":" ":") bbdb-separator-alist))
+(when (boundp 'bbdb-separator-alist)    ;Allow compilation if BBDB is absent!
+  (unless (assoc gnorb-bbdb-org-tag-field bbdb-separator-alist)
+    (push `(,gnorb-bbdb-org-tag-field ":" ":") bbdb-separator-alist)))
 
 (defcustom gnorb-bbdb-messages-field 'messages
   "The name (as a symbol) of the field where links to recent gnus
@@ -108,7 +107,7 @@ mentioned in the docstring of `format-time-string', which 
see."
   Defaults to org-link."
   :group 'gnorb-bbdb)
 
-(defstruct gnorb-bbdb-link
+(cl-defstruct gnorb-bbdb-link
   subject date group id)
 
 (defcustom gnorb-bbdb-posting-styles nil
@@ -206,6 +205,8 @@ Org tags are stored in the `gnorb-bbdb-org-tags-field'."
         (insert
          (bbdb-indent-string (concat val "\n") indent)))))))
 
+(defvar message-mode-hook)
+
 ;;;###autoload
 (defun gnorb-bbdb-mail (records &optional subject n verbose)
   "\\<bbdb-mode-map>Acts just like `bbdb-mail', except runs
@@ -420,14 +421,14 @@ a prefix arg and \"*\", the prefix arg must come first."
                             gnorb-gnus-mail-search-backends)
                      (error "No search backend specified")))
         (search-string
-         (funcall (second backend)
+         (funcall (cl-second backend)
                   (cl-mapcan 'bbdb-record-mail records))))
     (when (equal current-prefix-arg '(4))
       (setq search-string
            (read-from-minibuffer
             (format "%s search string: " (first backend)) search-string)))
-    (funcall (third backend) search-string)
-    (delete-other-windows)))  
+    (funcall (cl-third backend) search-string)
+    (delete-other-windows)))
 
 ;;;###autoload
 (defun gnorb-bbdb-cite-contact (rec)
@@ -438,8 +439,8 @@ a prefix arg and \"*\", the prefix arg must come first."
      mail-string)))
 
 ;;; Field containing links to recent messages
-
-(add-to-list 'bbdb-xfield-label-list gnorb-bbdb-messages-field nil 'eq)
+(when (boundp 'bbdb-xfield-label-list)
+ (add-to-list 'bbdb-xfield-label-list gnorb-bbdb-messages-field nil 'eq))
 
 (defun gnorb-bbdb-display-messages (record format)
   "Show links to the messages collected in the
@@ -594,16 +595,10 @@ to a message into the record's 
`gnorb-bbdb-messages-field'."
                          (parse-time-string (mail-header-date heads))))
             (subject (mail-header-subject heads))
             (id (mail-header-id heads))
-            (group gnus-newsgroup-name)
+            (group (gnorb-get-real-group-name
+                    gnus-newsgroup-name
+                    art-no))
             link)
-       ;; check for both nnvirtual and nnir, and link to the real
-       ;; group in those cases
-       (when (eq (car (gnus-find-method-for-group group))
-                 'nnvirtual)
-         (setq group (car (nnvirtual-map-article art-no))))
-       (when (eq (car (gnus-find-method-for-group group))
-                 'nnir)
-         (setq group (nnir-article-group art-no)))
        (if (not (and date subject id group))
            (message "Could not save a link to this message")
          (setq link (make-gnorb-bbdb-link :subject subject :date date
@@ -617,7 +612,7 @@ to a message into the record's `gnorb-bbdb-messages-field'."
                              (time-less-p
                               (gnorb-bbdb-link-date b)
                               (gnorb-bbdb-link-date a))))))
-         (setq val (cl-subseq val 0 gnorb-bbdb-collect-N-messages))
+         (setq val (cl-subseq val 0 (min (length val) 
gnorb-bbdb-collect-N-messages)))
          (bbdb-record-set-xfield record
                                  gnorb-bbdb-messages-field
                                  (delq nil val))
diff --git a/gnorb-gnus.el b/gnorb-gnus.el
index 4b7a16f..a77a7ed 100644
--- a/gnorb-gnus.el
+++ b/gnorb-gnus.el
@@ -1,6 +1,6 @@
 ;;; gnorb-gnus.el --- The gnus-centric fuctions of gnorb
 
-;; Copyright (C) 2014  Eric Abrahamsen
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Author: Eric Abrahamsen <address@hidden>
 ;; Keywords: 
@@ -24,9 +24,6 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
-
 (require 'gnus)
 (require 'gnorb-utils)
 
@@ -110,6 +107,12 @@ Basically behave as if all attachments have 
\":gnus-attachments t\"."
   :group 'gnorb-gnus
   :type 'string)
 
+(defcustom gnorb-gnus-summary-tracked-mark "&"
+  "Default mark to insert in the summary format line of articles
+  that are already tracked by TODO headings."
+  :group 'gnorb-gnus
+  :type 'string)
+
 (defcustom gnorb-gnus-trigger-refile-targets
   '((org-agenda-files :maxlevel . 4))
   "A value to use as an equivalent of `org-refile-targets' (which
@@ -192,7 +195,7 @@ save them into `gnorb-tmp-dir'."
       (set-buffer (org-capture-get :original-buffer)))
     (unless (memq major-mode '(gnus-summary-mode gnus-article-mode))
       (error "Only works in Gnus summary or article buffers"))
-    (let ((article (gnus-summary-article-number)) 
+    (let ((article (gnus-summary-article-number))
          mime-handles)
       (when (or (null gnus-current-article)
                (null gnus-article-current)
@@ -236,10 +239,13 @@ save them into `gnorb-tmp-dir'."
 
 (add-hook 'org-capture-mode-hook 'gnorb-gnus-capture-attach)
 
+(defvar org-note-abort)
+
 (defun gnorb-gnus-capture-abort-cleanup ()
   (with-no-warnings ; For `org-note-abort'
-   (when (and org-note-abort
-             (org-capture-get :gnus-attachments))
+    (when (and org-note-abort
+              (or gnorb-gnus-capture-always-attach
+                  (org-capture-get :gnus-attachments)))
      (condition-case error
         (progn (org-attach-delete-all)
                (setq abort-note 'clean)
@@ -299,11 +305,14 @@ information about the outgoing message into
            ;; `gnorb-org-setup-message' may have put this here, but
            ;; if we're working from a draft, or triggering this from
            ;; a reply, it might not be there yet.
-           (add-to-list 'message-exit-actions
+           (add-to-list 'message-send-actions
                         'gnorb-org-restore-after-send t))
        (setq gnorb-message-org-ids nil)))))
 
-(add-hook 'message-header-hook 'gnorb-gnus-check-outgoing-headers)
+;; This sets the global value, but the hook is made buffer-local in
+;; `gnus-inews-add-send-actions', so this is ignored
+;(add-hook 'message-header-hook 'gnorb-gnus-check-outgoing-headers)
+(add-hook 'message-send-hook 'gnorb-gnus-check-outgoing-headers t)
 
 ;;;###autoload
 (defun gnorb-gnus-outgoing-do-todo (&optional arg)
@@ -381,10 +390,9 @@ work."
          (save-excursion
            (save-restriction
              (widen)
-             (setq message-exit-actions
-                   (remove 'gnorb-org-restore-after-send
-                           (remove 'gnorb-gnus-outgoing-make-todo-1
-                                   message-exit-actions)))
+             (setq message-send-actions
+                   (remove 'gnorb-gnus-outgoing-make-todo-1
+                           message-send-actions))
              (message-narrow-to-headers-or-head)
              (message-remove-header
               gnorb-mail-header)
@@ -424,17 +432,16 @@ work."
                ;; message
                (push h header-ids)))))
        (goto-char compose-marker)
-       (add-to-list
-        'message-exit-actions
-        (if header-ids
-            'gnorb-org-restore-after-send
-          'gnorb-gnus-outgoing-make-todo-1)
-        t)
+       (unless header-ids
+         (add-to-list 'message-send-actions
+          'gnorb-gnus-outgoing-make-todo-1 t))
        (message
         (if header-ids
             "Message will trigger TODO state-changes after sending"
           "A TODO will be made from this message after it's sent"))))))
 
+(defvar org-capture-link-is-already-stored)
+
 (defun gnorb-gnus-outgoing-make-todo-1 ()
   (unless gnorb-gnus-new-todo-capture-key
     (error "No capture template key set, customize 
gnorb-gnus-new-todo-capture-key"))
@@ -486,18 +493,21 @@ work."
 (defun gnorb-gnus-incoming-do-todo (arg &optional id)
   "Call this function from a received gnus message to store a
 link to the message, prompt for a related Org heading, visit the
-heading, and either add a note or trigger a TODO state change.
-Set `gnorb-trigger-todo-default' to 'note or 'todo (you can
-get the non-default behavior by calling this function with a
-prefix argument), or to 'prompt to always be prompted.
-
-In some cases, Gnorb can guess for you which Org heading you
-probably want to trigger, which can save some time. It does this
-by looking in the References header, and seeing if any of the IDs
-there match the value of the `gnorb-org-msg-id-key' property for
-any headings. In order for this to work, you will have to have
-loaded org-id, and have the variable `org-id-track-globally' set
-to t (it is, by default)."
+heading, and trigger an action on it \(see
+`gnorb-org-trigger-actions'\).
+
+If you've set up message tracking \(with
+`gnorb-tracking-initialize'\), Gnorb can guess which Org heading
+you probably want to trigger, which can save some time. It does
+this by looking in the References header, and seeing if any of
+the messages referenced there are already being tracked by any
+headings.
+
+If you mark several messages before calling this function, or
+call it with a numerical prefix arg, those messages will be
+\"bulk associated\" with the chosen Org heading: associations
+will be made, but you won't be prompted to trigger an action, and
+you'll stay in the Gnus summary buffer."
   (interactive "P")
   (when (not (memq major-mode '(gnus-summary-mode gnus-article-mode)))
     (user-error "Only works in gnus summary or article mode"))
@@ -507,15 +517,18 @@ to t (it is, by default)."
   (setq gnorb-window-conf (current-window-configuration))
   (move-marker gnorb-return-marker (point))
   (setq gnorb-gnus-message-info nil)
-  (let* ((headers (gnus-data-header
-                  (gnus-data-find
-                   (gnus-summary-article-number))))
+  (let* ((articles (gnus-summary-work-articles arg))
+        (art-no (gnus-summary-article-number))
+        (headers (gnus-data-header
+                  (gnus-data-find art-no)))
         (msg-id (mail-header-id headers))
         (from (mail-header-from headers))
         (subject (mail-header-subject headers))
         (date (mail-header-date headers))
         (to (cdr (assoc 'To (mail-header-extra headers))))
-        (group gnus-newsgroup-name)
+        (group (gnorb-get-real-group-name
+                gnus-newsgroup-name
+                art-no))
         (link (call-interactively 'org-store-link))
         (org-refile-targets gnorb-gnus-trigger-refile-targets)
         (ref-msg-ids (concat (mail-header-references headers) " "
@@ -532,37 +545,109 @@ to t (it is, by default)."
                     :link ,link :date ,date :refs ,ref-msg-ids
                     :group ,group))
     (gnorb-gnus-collect-all-attachments nil t)
-    ;; Delete other windows, users can restore with
-    ;; `gnorb-restore-layout'.
-    (delete-other-windows)
-    (if id
-       (gnorb-trigger-todo-action arg id)
-      ;; Flush out zombies (dead associations).
-      (setq related-headings
-           (cl-remove-if
-            (lambda (h)
-              (when (null (org-id-find-id-file h))
-                (when (y-or-n-p
-                       (format
-                        "ID %s no longer exists, disassociate message?"
-                        h))
-                  (gnorb-delete-association msg-id h))))
-            related-headings))
-      (if (catch 'target
-           (dolist (h related-headings nil)
-             (when (yes-or-no-p
-                    (format "Trigger action on %s"
-                            (gnorb-pretty-outline h)))
-               (throw 'target (setq targ h)))))
-         (gnorb-trigger-todo-action arg targ)
-       (setq targ (org-refile-get-location
-                   "Trigger heading" nil t))
-       (find-file (nth 1 targ))
-       (goto-char (nth 3 targ))
-       (gnorb-trigger-todo-action arg)))))
+    (condition-case err
+     (if id
+        (progn
+          (delete-other-windows)
+          (gnorb-trigger-todo-action nil id))
+       ;; Flush out zombies (dead associations).
+       (setq related-headings
+            (cl-remove-if
+             (lambda (h)
+               (when (null (org-id-find-id-file h))
+                 (when (y-or-n-p
+                        (format
+                         "ID %s no longer exists, disassociate message?"
+                         h))
+                   (gnorb-delete-association msg-id h))))
+             related-headings))
+       ;; See if one of the related headings is chosen.
+       (unless (catch 'target
+                (dolist (h related-headings nil)
+                  (when (yes-or-no-p
+                         (format "Trigger action on %s"
+                                 (gnorb-pretty-outline h)))
+                    (throw 'target (setq targ h)))))
+        ;; If not, use the refile interface to choose one.
+        (setq targ (org-refile-get-location
+                    "Trigger heading" nil t))
+        (setq targ
+              (save-window-excursion
+                (find-file (nth 1 targ))
+                (goto-char (nth 3 targ))
+                (org-id-get-create))))
+       ;; Either bulk associate multiple messages...
+       (if (> (length articles) 1)
+          (progn
+            (dolist (a articles)
+              (gnorb-registry-make-entry
+               (mail-header-id
+                (gnus-data-header
+                 (gnus-data-find a)))
+               from subject targ group)
+              (gnus-summary-remove-process-mark a))
+            (message "Associated %d messages with %s"
+                     (length articles) (gnorb-pretty-outline targ)))
+        ;; ...or just trigger the one.
+        (delete-other-windows)
+        (gnorb-trigger-todo-action nil targ)))
+     (error
+      ;; If these are left populated after an error, it plays hell
+      ;; with future trigger processes.
+      (setq gnorb-gnus-message-info nil)
+      (setq gnorb-gnus-capture-attachments nil)
+      (signal (car err) (cdr err))))))
 
 ;;;###autoload
-(defun gnorb-gnus-search-messages (str &optional ret)
+(defun gnorb-gnus-quick-reply ()
+  "Compose a reply to the message under point, and associate both
+the original message and the reply with the selected heading.
+Take no other action.
+
+Use this when you want to compose a reply to a message on the
+spot, and track both messages, without having to go through the
+hassle of triggering an action on a heading, and then starting a
+reply."
+  (interactive)
+  (when (not (memq major-mode '(gnus-summary-mode gnus-article-mode)))
+    (user-error "Only works in gnus summary or article mode"))
+  (let* ((art-no (gnus-summary-article-number))
+        (headers (gnus-data-header
+                  (gnus-data-find art-no)))
+        (msg-id (mail-header-id headers))
+        (from (mail-header-from headers))
+        (subject (mail-header-subject headers))
+        (group (gnorb-get-real-group-name
+                gnus-newsgroup-name
+                art-no))
+        (ref-msg-ids (concat (mail-header-references headers) " "
+                             msg-id))
+        (related-headings
+         (when ref-msg-ids
+           (gnorb-find-tracked-headings headers t)))
+        (targ (car-safe related-headings)))
+    (if targ
+       (let ((ret (make-marker)))
+         ;; Assume the first heading is the one we want.
+         (gnorb-registry-make-entry
+          msg-id from subject targ group)
+         (gnus-summary-wide-reply-with-original 1)
+         (move-marker ret (point))
+         (save-restriction
+           (widen)
+           (message-narrow-to-headers-or-head)
+           (goto-char (point-min))
+           (open-line 1)
+           (message-insert-header
+            (intern gnorb-mail-header) targ))
+         (goto-char ret)
+         (message
+          (format "Original message and reply will be associated with %s"
+                  (gnorb-pretty-outline targ))))
+      (message "No associated headings found"))))
+
+;;;###autoload
+(defun gnorb-gnus-search-messages (str persist &optional head-text ret)
   "Initiate a search for gnus message links in an org subtree.
 The arg STR can be one of two things: an Org heading id value
 \(IDs should be prefixed with \"id+\"\), in which case links will
@@ -579,31 +664,46 @@ work."
   (let ((nnir-address
         (or (gnus-method-to-server '(nngnorb))
             (user-error
-             "Please add a \"nngnorb\" backend to your gnus installation."))))
+             "Please add a \"nngnorb\" backend to your gnus installation.")))
+       name method spec)
     (when (version= "5.13" gnus-version-number)
       (with-no-warnings                  ; All these variables are available.
        (setq nnir-current-query nil
              nnir-current-server nil
              nnir-current-group-marked nil
              nnir-artlist nil)))
-    (gnus-group-read-ephemeral-group
-     ;; in 24.4, the group name is mostly decorative. in 24.3, the
-     ;; query itself is read from there. It should look like (concat
-     ;; "nnir:" (prin1-to-string '((query str))))
-     (if (version= "5.13" gnus-version-number)
-        (concat "nnir:" (prin1-to-string `((query ,str))))
-       (concat "gnorb-" str))
-     (if (version= "5.13" gnus-version-number)
-        (list 'nnir nnir-address)
-       (list 'nnir "nnir"))
-     nil
-     ret
-     nil nil
-     ;; the following seems to simply be ignored under gnus 5.13
-     (list (cons 'nnir-specs (list (cons 'nnir-query-spec `((query . ,str)))
+    ;; In 24.4, the group name is mostly decorative, but in 24.3, the
+    ;; actual query is held there.
+    (setq name (if (version= "5.13" gnus-version-number)
+                  (concat "nnir:" (prin1-to-string `((query ,str))))
+                (if persist
+                    (read-string
+                     (format "Name for group (default %s): " head-text)
+                     nil head-text t)
+                  (concat "gnorb-" str))))
+    (setq method (if (version= "5.13" gnus-version-number)
+                    (list 'nnir nnir-address)
+                  (list 'nnir "Gnorb")))
+    (setq spec
+         (list
+          (cons 'nnir-specs (list (cons 'nnir-query-spec `((query . ,str)))
                                   (cons 'nnir-group-spec `((,nnir-address 
nil)))))
           (cons 'nnir-artlist nil)))
-    (gnorb-summary-minor-mode)))
+    (if persist
+       (progn
+         (switch-to-buffer gnus-group-buffer)
+         (gnus-group-make-group name method nil spec)
+         (gnus-group-select-group))
+     (gnus-group-read-ephemeral-group name method nil ret nil nil spec))))
+
+(defun gnorb-gnus-summary-mode-hook ()
+  "Check if we've entered a Gnorb-generated group, and activate
+  `gnorb-summary-minor-mode', if so."
+  (let ((method (gnus-find-method-for-group gnus-newsgroup-name)))
+    (when (string-match-p "Gnorb" (cadr method))
+      (gnorb-summary-minor-mode))))
+
+(add-hook 'gnus-summary-mode-hook #'gnorb-gnus-summary-mode-hook)
 
 ;;; Automatic noticing of relevant messages
 
@@ -633,8 +733,7 @@ option `gnorb-gnus-hint-relevant-article' is non-nil."
           (tracked-headings (gnorb-find-tracked-headings headers))
           (key
            (where-is-internal 'gnorb-gnus-incoming-do-todo
-                              nil t))
-          rel-headings)
+                              nil t)))
       (cond (assoc-heading
             (message "Message is associated with %s"
                      (gnorb-pretty-outline (car assoc-heading) t)))
@@ -652,9 +751,12 @@ option `gnorb-gnus-hint-relevant-article' is non-nil."
   (if (not (memq (car (gnus-find-method-for-group
                       gnus-newsgroup-name))
                 '(nnvirtual nnir)))
-      (if (gnorb-find-tracked-headings header)
-         gnorb-gnus-summary-mark
-       " ")
+      (cond ((gnus-registry-get-id-key
+             (mail-header-message-id header) 'gnorb-ids)
+            gnorb-gnus-summary-tracked-mark)
+           ((gnorb-find-tracked-headings header)
+            gnorb-gnus-summary-mark)
+           (t " "))
     " "))
 
 (fset (intern (concat "gnus-user-format-function-"
@@ -666,11 +768,11 @@ option `gnorb-gnus-hint-relevant-article' is non-nil."
 (defun gnorb-gnus-view ()
   "Display the first relevant TODO heading for the message under point"
   (interactive)
-  (let ((headers (gnus-data-header
+  (let* ((headers (gnus-data-header
                   (gnus-data-find
                    (gnus-summary-article-number))))
-       (tracked-headings
-        (gnorb-find-tracked-headings headers)))
+        (tracked-headings
+         (gnorb-find-tracked-headings headers)))
     (when tracked-headings
       (setq gnorb-window-conf (current-window-configuration))
       (move-marker gnorb-return-marker (point))
diff --git a/gnorb-org.el b/gnorb-org.el
index 846ded9..d54e9ba 100644
--- a/gnorb-org.el
+++ b/gnorb-org.el
@@ -1,6 +1,6 @@
 ;;; gnorb-org.el --- The Org-centric functions of gnorb
 
-;; Copyright (C) 2014  Eric Abrahamsen
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Author: Eric Abrahamsen  <address@hidden>
 ;; Keywords: 
@@ -24,10 +24,8 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
-
 (require 'gnorb-utils)
+(require 'cl-lib)
 
 (defgroup gnorb-org nil
   "The Org bits of Gnorb."
@@ -45,9 +43,8 @@
     ("take note" . note)
     ("don't associate" . no-associate)
     ("only associate" . associate)
-;    ("capture to child" . cap-child)
-;    ("capture to sibling" . cap-sib)
-)
+    ("capture to child" . cap-child)
+    ("capture to sibling" . cap-sib))
   "List of potential actions that can be taken on headings.
 
 When triggering an Org heading after receiving or sending a
@@ -58,8 +55,8 @@ todo state: Associate the message, and change TODO state.
 take note: Associate the message, and take a note.
 don't associate: Do nothing at all, don't connect the message and TODO.
 only associate: Associate the message with this heading, do nothing else.
-capture to child: [not yet implemented] Associate this message with a new 
child heading.
-capture to sibling: [not yet implemented] Associate this message with a new 
sibling heading.
+capture to child: Associate this message with a new child heading.
+capture to sibling: Associate this message with a new sibling heading.
 
 You can reorder this list or remove items as suits your workflow.
 The two \"capture\" options will use the value of
@@ -179,9 +176,10 @@ we came from."
            (cond ((eq gnorb-org-mail-scan-scope 'all)
                   strings)
                  ((numberp gnorb-org-mail-scan-scope)
-                  (delq nil
-                        (cl-subseq
-                         strings 0 (1+ gnorb-org-mail-scan-scope))))
+                  (cl-subseq
+                   strings 0 (min
+                              (length strings)
+                              (1+ gnorb-org-mail-scan-scope))))
                  ;; We could provide more options here. 'tree vs
                  ;; 'subtree, for instance.
                  (t
@@ -246,6 +244,8 @@ See the docstring of `gnorb-org-handle-mail' for details."
      (msg-id-link
       `(:gnus ,(list msg-id-link))))))
 
+(defvar message-beginning-of-line)
+
 (defun gnorb-org-setup-message
     (&optional messages mails from cc bcc attachments text ids)
   "Common message setup routine for other gnorb-org commands.
@@ -271,10 +271,14 @@ headings."
     (when messages
       (insert ", "))
     (insert (mapconcat 'identity mails ", ")))
-  ;; Return us after message is sent.
-  (add-to-list 'message-exit-actions
-              'gnorb-org-restore-after-send t)
-  ;; Set headers from MAIL_* properties (from, cc, and bcc).
+  ;; Commenting this out because
+  ;; `gnorb-gnus-check-outgoing-headers' is set unconditionally in the
+  ;; `message-send-hook, so this should be redundant.  Also, we've
+  ;; switched to using message-send-actions.
+  
+  ;; (add-to-list
+  ;; 'message-exit-actions 'gnorb-org-restore-after-send t) Set
+  ;; headers from MAIL_* properties (from, cc, and bcc).
   (cl-flet ((sh (h)
                (when (cdr h)
                  (funcall (intern (format "message-goto-%s" (car h))))
@@ -338,6 +342,8 @@ current heading, or the heading indicated by optional 
argument ID."
             (org-attach-file-list attach-dir))))
       files)))
 
+(defvar message-mode-hook)
+
 ;;;###autoload
 (defun gnorb-org-handle-mail (&optional arg text file)
   "Handle current headline as a mail TODO.
@@ -475,6 +481,8 @@ respective (usual) file extensions. Ugly way to do it, but 
what
 the hey..."
   :group 'gnorb-org)
 
+(defvar org-export-show-temporary-export-buffer)
+
 ;;;###autoload
 (defun gnorb-org-email-subtree (&optional arg)
   "Call on a subtree to export it either to a text string or a file,
@@ -517,7 +525,7 @@ default set of parameters."
            (apply 'org-export-to-file
                   `(,backend-symbol
                     ,(org-export-output-file-name
-                      (second (assoc backend-symbol 
gnorb-org-export-extensions))
+                      (cl-second (assoc backend-symbol 
gnorb-org-export-extensions))
                       t gnorb-tmp-dir)
                     ,@opts
                     ,gnorb-org-email-subtree-file-parameters))))
@@ -609,7 +617,9 @@ search."
                        (let ((rec-tags (bbdb-record-xfield
                                         r gnorb-bbdb-org-tag-field)))
                          (and rec-tags
-                              (let ((tags-list (org-split-string rec-tags ":"))
+                              (let ((tags-list (if (stringp rec-tags)
+                                                   (org-split-string rec-tags 
":")
+                                                 rec-tags))
                                     (case-fold-search t)
                                     (org-trust-scanner-tags t))
                                 (eval tag-clause)))))
@@ -641,14 +651,17 @@ search."
 ;;; Groups from the gnorb gnus server backend
 
 ;;;###autoload
-(defun gnorb-org-view ()
+(defun gnorb-org-view (arg)
   "Search the subtree at point for links to gnus messages, and
-then show them in an ephemeral group, in gnus.
+then show them in an ephemeral group, in Gnus.
+
+With a prefix arg, create a search group that will persist across
+Gnus sessions, and can be refreshed.
 
 This won't work unless you've added a \"nngnorb\" server to
 your gnus select methods."
   ;; this should also work on the active region, if there is one.
-  (interactive)
+  (interactive "P")
   (require 'gnorb-gnus)
   (setq gnorb-window-conf (current-window-configuration))
   (move-marker gnorb-return-marker (point))
@@ -667,7 +680,10 @@ your gnus select methods."
       (org-back-to-heading)
       (setq id (concat "id+" (org-id-get-create)))
       (gnorb-gnus-search-messages
-       id
+       id arg
+       (replace-regexp-in-string
+       org-bracket-link-regexp "\\3"
+       (nth 4 (org-heading-components)))
        `(when (and (window-configuration-p gnorb-window-conf)
                   gnorb-return-marker)
          (set-window-configuration gnorb-window-conf)
diff --git a/gnorb-registry.el b/gnorb-registry.el
index f70531c..bcd5adc 100644
--- a/gnorb-registry.el
+++ b/gnorb-registry.el
@@ -1,6 +1,6 @@
 ;;; gnorb-registry.el --- Registry implementation for Gnorb
 
-;; This file is in the public domain.
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Author: Eric Abrahamsen <address@hidden>
 
@@ -49,6 +49,8 @@
 ;;; Code:
 
 (require 'gnus-registry)
+(require 'gnorb-utils)
+(require 'cl-lib)
 
 (defgroup gnorb-registry nil
   "Gnorb's use of the Gnus registry."
@@ -84,15 +86,11 @@ to the message's registry entry, under the 'gnorb-ids key."
               (memq major-mode '(gnus-summary-mode gnus-article-mode)))
             (not org-note-abort))
     (let* ((msg-id
-           (format "<%s>" (plist-get org-store-link-plist :message-id)))
-          (entry (gnus-registry-get-or-make-entry msg-id))
-          (org-ids
-           (gnus-registry-get-id-key msg-id 'gnorb-ids))
-          (new-org-id (org-id-get-create)))
-      (plist-put org-capture-plist :gnorb-id new-org-id)
-      (setq org-ids (cons new-org-id org-ids))
-      (setq org-ids (delete-dups org-ids))
-      (gnus-registry-set-id-key msg-id 'gnorb-ids org-ids))))
+           (gnorb-bracket-message-id
+            (plist-get org-store-link-plist :message-id)))
+          (org-id (org-id-get-create)))
+      (plist-put org-capture-plist :gnorb-id org-id)
+      (gnorb-registry-make-entry msg-id nil nil org-id nil))))
 
 
 (defun gnorb-registry-capture-abort-cleanup ()
@@ -148,25 +146,113 @@ the MSG-ID."
       (gnus-registry-set-id-key msg-id 'gnorb-ids
                                (remove org-id org-ids)))))
 
-(defun gnorb-delete-all-assocations (org-id)
+(defun gnorb-delete-all-associations (org-id)
   "Delete all message associations for an Org heading.
 
 The heading is identified by ORG-ID. This is suitable for use
 after an Org heading is deleted, for instance."
-  (let ((assoc-msgs (gnorb-registry-org-id-search org-id)))
+  (let ((assoc-msgs (gnorb-registry-org-id-search org-id))
+       (gnorb-id-tracker
+        (registry-lookup-secondary gnus-registry-db 'gnorb-ids)))
     (mapcar
      (lambda (msg-id)
        (let ((org-ids
              (gnus-registry-get-id-key msg-id 'gnorb-ids)))
         (gnus-registry-set-id-key
          msg-id 'gnorb-ids (remove org-id org-ids))))
-     assoc-msgs)))
+     assoc-msgs)
+    (remhash org-id gnorb-id-tracker)))
+
+(defun gnorb-flush-dead-associations (&optional clean-archived)
+  "Clean the registry of associations with nonexistent headings.
+
+Gnus will not prune registry entries that appear to be associated
+with an Org heading.  If your registry is limited to a very small
+size, you may end up with a full registry.  Use this function to
+remove dead associations, and free up more entries for possible
+pruning.
+
+By default, associations are considered \"live\" if the Org
+heading exists in an Org file or in an Org archive file.  When
+optional CLEAN_ARCHIVED is non-nil, delete associations from
+archived headings as well."
+  (interactive "P")
+  (let ((gnorb-id-tracker
+        (registry-lookup-secondary gnus-registry-db 'gnorb-ids))
+       (deleted-count 0))
+    (require 'org-id)
+    (maphash
+     (lambda (k _)
+       (let ((file (org-id-find-id-file k)))
+        (when (or (not file)
+                  (and clean-archived
+                       (string-match-p "org_archive$" file)))
+          (gnorb-delete-all-associations k)
+          (incf deleted-count))))
+     gnorb-id-tracker)
+    (message "Deleted %d invalid associations"
+            deleted-count)))
 
 (defun gnorb-registry-org-id-search (id)
   "Find all messages that have the org ID in their 'gnorb-ids
 key."
   (registry-search gnus-registry-db :member `((gnorb-ids ,id))))
 
+(defun gnorb-registry-tracked-messages ()
+  "Return all message-ids that have non-empty 'gnorb-ids keys."
+  (registry-search gnus-registry-db :regex `((gnorb-ids ".+"))))
+
+(defun gnorb-registry-tracked-headings ()
+  "Return all Org heading ids that are associated with messages."
+  (hash-table-keys
+   (registry-lookup-secondary gnus-registry-db 'gnorb-ids)))
+
+(defun gnorb-report-tracking-usage ()
+  "Pop up a temporary window reporting on Gnorb usage of the Gnus
+registry to track message/heading associations.  Reports the
+number of tracked messages, the number of tracked headings, and how much of 
the registry is occupied."
+  (interactive)
+  (progn
+    (pop-to-buffer
+     (get-buffer-create "*Gnorb Usage*")
+     '(nil . ((window-height . 10))))
+    (gnorb-refresh-usage-status)
+    (special-mode)
+    (setq revert-buffer-function #'gnorb-refresh-usage-status)
+    (local-set-key (kbd "d") (lambda ()
+                              (interactive)
+                              (progn
+                                (gnorb-flush-dead-associations)
+                                (gnorb-refresh-usage-status))))
+    (local-set-key (kbd "D") (lambda ()
+                              (interactive)
+                              (progn
+                                (gnorb-flush-dead-associations t)
+                                (gnorb-refresh-usage-status))))))
+
+(defun gnorb-refresh-usage-status (&optional ignore-auto noconfirm)
+  "Clear and re-format the *Gnorb Usage* buffer."
+  (let ((messages (length (gnorb-registry-tracked-messages)))
+       (headings (length (gnorb-registry-tracked-headings)))
+       (reg-size (registry-size gnus-registry-db))
+       (reg-max-size (oref gnus-registry-db max-size)))
+    (with-current-buffer "*Gnorb Usage*"
+      (let ((inhibit-read-only t))
+       (erase-buffer)
+       (insert
+       (format
+        "Tracking %d Gnus messages associated with %d Org headings."
+        messages headings))
+       (insert "\n\n")
+       (insert
+       (format
+        "Occupying %.2f%% (%d/%d) of the registry (max %d)."
+        (* 100 (/ (float messages) reg-size))
+        messages reg-size reg-max-size))
+       (insert "\n\n")
+       (insert "Press 'd' to delete associations for non-existent Org 
headings.\n")
+       (insert "Press 'D' to delete associations for both non-existent and 
archived Org headings.")))))
+
 (defun gnorb-registry-transition-from-props (arg)
   "Helper function for transitioning the old tracking system to the new.
 
@@ -202,8 +288,8 @@ your Org files."
                       'gnus))
          (dolist (l (plist-get links :gnus))
            (gnorb-registry-make-entry
-            (second (split-string l "#")) nil nil
-            id (first (split-string l "#"))))
+            (cl-second (split-string l "#")) nil nil
+            id (cl-first (split-string l "#"))))
          (dolist (p props)
            (setq id )
            (gnorb-registry-make-entry p nil nil id nil)
@@ -212,7 +298,7 @@ your Org files."
            ;; it.
            (unless (gnus-registry-get-id-key p 'group)
              (gnorb-msg-id-to-group p))
-           (incf count)))))
+           (cl-incf count)))))
      gnorb-org-find-candidates-match
      'agenda 'archive 'comment)
     (message "Collecting all relevant Org headings, this could take a while... 
done")
diff --git a/gnorb-utils.el b/gnorb-utils.el
index 81efa32..d7f5e86 100644
--- a/gnorb-utils.el
+++ b/gnorb-utils.el
@@ -1,6 +1,6 @@
 ;;; gnorb-utils.el --- Common utilities for all gnorb stuff.
 
-;; Copyright (C) 2014  Eric Abrahamsen
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Author: Eric Abrahamsen <address@hidden>
 ;; Keywords:
@@ -24,9 +24,9 @@
 
 ;;; Code:
 
-(eval-when-compile
-  (require 'cl))
+(require 'cl-lib)
 
+(require 'mailcap)
 (mailcap-parse-mimetypes)
 
 (defgroup gnorb nil
@@ -74,6 +74,11 @@ are sent, or Org headings triggered.")
   "Return point here after various actions, to be used together
 with `gnorb-window-conf'.")
 
+(defvar gnorb-trigger-capture-location nil
+  "Marker pointing at the location where we want to place capture
+  templates, for the capture-to-child and capture-to-sibling
+  trigger actions.")
+
 (defcustom gnorb-mail-header "X-Org-ID"
   "Name of the mail header used to store the ID of a related Org
   heading. Only used locally: always stripped when the mail is
@@ -213,38 +218,32 @@ window."
 we were in the agenda when this was called, then keep us in the
 agenda. Then let the user choose an action from the value of
 `gnorb-org-trigger-actions'."
-  (let ((agenda-p (eq major-mode 'org-agenda-mode))
-       (action (cdr (assoc
-                     (org-completing-read
-                      "Action to take: "
-                      gnorb-org-trigger-actions nil t)
-                     gnorb-org-trigger-actions)))
-       (root-marker (make-marker)))
-    ;; Place the marker for the relevant TODO heading.
-    (cond (agenda-p
-          (setq root-marker
+  (let* ((agenda-p (eq major-mode 'org-agenda-mode))
+        (root-marker
+         (cond (agenda-p
                 (copy-marker
-                 (org-get-at-bol 'org-hd-marker))))
-         ((derived-mode-p 'org-mode)
-          (move-marker root-marker (point-at-bol)))
-         (id
-          (save-excursion
-            (org-id-goto id)
-            (move-marker root-marker (point-at-bol)))))
+                 (org-get-at-bol 'org-hd-marker)))
+               ((derived-mode-p 'org-mode)
+                (save-excursion
+                  (org-back-to-heading)
+                  (point-marker)))
+               (id
+                (save-excursion
+                  (org-id-goto id)
+                  (org-back-to-heading)
+                  (point-marker)))))
+        (id (or id
+                (org-with-point-at root-marker
+                  (org-id-get-create))))
+        (action (cdr (assoc
+                      (org-completing-read
+                       (format
+                        "Trigger action on %s: "
+                        (gnorb-pretty-outline id))
+                       gnorb-org-trigger-actions nil t)
+                      gnorb-org-trigger-actions))))
     (unless agenda-p
       (org-reveal))
-    ;; Query about attaching email attachments. No matter what
-    ;; happens, clear `gnorb-gnus-capture-attachments'.
-    (unwind-protect
-       (org-with-point-at root-marker
-         (map-y-or-n-p
-          (lambda (a)
-            (format "Attach %s to heading? "
-                    (file-name-nondirectory a)))
-          (lambda (a) (org-attach-attach a nil 'mv))
-          gnorb-gnus-capture-attachments
-          '("file" "files" "attach")))
-      (setq gnorb-gnus-capture-attachments nil))
     (cl-labels
        ((make-entry
          (id)
@@ -255,28 +254,81 @@ agenda. Then let the user choose an action from the value 
of
           id
           (plist-get gnorb-gnus-message-info :group))))
       ;; Handle our action.
-      (cond ((eq action 'note)
-            (org-with-point-at root-marker
-              (make-entry (org-id-get-create))
-              (call-interactively 'org-add-note)))
-           ((eq action 'todo)
-            (if agenda-p
-                (progn
-                  (org-with-point-at root-marker
-                   (make-entry (org-id-get-create)))
-                  (call-interactively 'org-agenda-todo))
-              (org-with-point-at root-marker
-                (make-entry (org-id-get-create))
-                (call-interactively 'org-todo))))
-           ((eq action 'no-associate)
-            nil)
-           ((eq action 'associate)
-            (org-with-point-at root-marker
-              (make-entry (org-id-get-create))))
-           ((fboundp action)
+      (if (fboundp action)
+         (org-with-point-at root-marker
+           (make-entry (org-id-get-create))
+           (funcall action gnorb-gnus-message-info))
+       (cl-case action
+         (note
+          (org-with-point-at root-marker
+            (make-entry (org-id-get-create))
+            (call-interactively 'org-add-note)))
+         (todo
+          (if agenda-p
+              (progn
+                (org-with-point-at root-marker
+                  (make-entry (org-id-get-create)))
+                (call-interactively 'org-agenda-todo))
             (org-with-point-at root-marker
               (make-entry (org-id-get-create))
-              (funcall action gnorb-gnus-message-info)))))))
+              (call-interactively 'org-todo))))
+         (no-associate
+          nil)
+         (associate
+          (org-with-point-at root-marker
+            (make-entry (org-id-get-create))))
+         ;; We're going to capture a new heading
+         ((cap-child cap-sib)
+          (org-with-point-at root-marker
+               (setq gnorb-trigger-capture-location (point-marker)))
+          (let ((entry
+                 ;; Pick a template.
+                 (copy-sequence (org-capture-select-template))))
+            ;; Do surgery on that template so that it finds its
+            ;; location using our function.
+            (setf (nth 3 entry)
+                  `(function
+                    ,(if (eq action 'cap-child)
+                         #'gnorb-trigger-capture-child
+                       #'gnorb-trigger-capture-sibling)))
+            ;; This will likely fail horribly for capture templates
+            ;; that aren't entries or list items.
+            (let ((org-capture-entry entry))
+              ;; When org-capture-entry is let-bound, the capture
+              ;; process will use that template instead of
+              ;; prompting the user. Also, `gnorb-registry-capture'
+              ;; will take care of making the registry entry for us.
+              (call-interactively 'org-capture)))))))
+    ;; Lastly, query about attaching email attachments. No matter what
+    ;; happens, clear `gnorb-gnus-capture-attachments'.
+    (unwind-protect
+       (org-with-point-at
+           (if (memq action '(cap-child cap-sib))
+               (point)
+             root-marker)
+         (map-y-or-n-p
+          (lambda (a)
+            (format "Attach %s to heading? "
+                    (file-name-nondirectory a)))
+          (lambda (a)
+            (with-demoted-errors
+                (org-attach-attach a nil 'mv)))
+          gnorb-gnus-capture-attachments
+          '("file" "files" "attach")))
+      (setq gnorb-gnus-capture-attachments nil))))
+
+(defun gnorb-trigger-capture-child ()
+  ;; The capture process creates a child by default
+  (org-goto-marker-or-bmk gnorb-trigger-capture-location)
+  (org-back-to-heading))
+
+(defun gnorb-trigger-capture-sibling ()
+  ;; This only works if we're not trying to create a sibling for a
+  ;; top-level heading, there appears to be no way to do that.  But in
+  ;; that case this trigger action isn't really necessary, just
+  ;; handle it with a regular capture.
+  (org-goto-marker-or-bmk gnorb-trigger-capture-location)
+  (org-up-heading-safe))
 
 (defun gnorb-pretty-outline (id &optional kw)
   "Return pretty outline path of the Org heading indicated by ID.
@@ -364,8 +416,7 @@ methods?"
                     (ignore-errors
                       (gnus-request-head msg-id server-group)))
                (throw 'found server-group))))
-      (when (featurep 'notmuch)
-       nil))))
+      nil)))
 
 (defun gnorb-collect-ids (&optional id)
   "Collect all Org IDs for a subtree.
@@ -386,6 +437,16 @@ child headings."
 ;; Common functions for extracting references and relevant headings
 ;; from the message under point. For use in gnorb-gnus.el functions.
 
+(defun gnorb-get-real-group-name (group art-no)
+  "Find the original group name of a message in a virtual or nnir
+group."
+  (cl-case (car (gnus-find-method-for-group group))
+    (nnvirtual
+     (setq group (car (nnvirtual-map-article art-no))))
+    (nnir
+     (setq group (nnir-article-group art-no))))
+  group)
+
 (defun gnorb-find-tracked-headings (headers &optional include-zombies)
   "Check HEADERS for message references and return relevant heading IDs.
 
diff --git a/gnorb.el b/gnorb.el
index 6238623..cb7d908 100644
--- a/gnorb.el
+++ b/gnorb.el
@@ -1,8 +1,9 @@
 ;;; gnorb.el --- Glue code between Gnus, Org, and BBDB
 
-;; Copyright (C) 2014  Eric Abrahamsen
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Version: 1.0.1
+;; Package-Requires: ((cl-lib "0.5"))
 
 ;; Maintainer: Eric Abrahamsen <address@hidden>
 
@@ -31,13 +32,13 @@
 ;;; Code:
 
 (with-eval-after-load 'gnus
- (require 'nngnorb)
- (require 'gnorb-gnus)
- (require 'gnorb-registry))
+  (require 'nngnorb)
+  (require 'gnorb-gnus)
+  (require 'gnorb-registry))
 (with-eval-after-load 'bbdb
   (require 'gnorb-bbdb))
 (with-eval-after-load 'org
- (require 'gnorb-org))
+  (require 'gnorb-org))
 
 
 (provide 'gnorb)
diff --git a/gnorb.info b/gnorb.info
index 08085e1..52ca072 100644
--- a/gnorb.info
+++ b/gnorb.info
@@ -30,11 +30,13 @@ Gnorb Manual
 
 Email Tracking
 
+* Basic Usage::
 * Email-Related Commands::
 * Trigger Actions::
 * Viewing Tracked Messages in *Summary* Buffers::
 * Hinting in Gnus::
 * Message Attachments::
+* Registry Usage::
 * Likely Workflow::
 
 Misc BBDB
@@ -138,29 +140,56 @@ IDs are associated with Org heading ids.  As a 
conversation develops,
 messages are collected on a heading (and/or its children).  You can
 compose new messages directly from the Org heading, and Gnorb will
 automatically associate your sent message with the conversation.  You
-can open temporary Gnus *Summary* buffers holding all the messages
-associated with an Org subtree, and reply from there.  When you receive
-new messages relevant to a conversation, Gnorb will notice them and
-prompt you to associate them with the appropriate Org heading.
-Attachments on incoming messages can be automatically saved as
-attachments on Org headings, using org-attach.
+can open Gnus *Summary* buffers holding all the messages associated with
+an Org subtree, and reply from there – these groups can be made
+persistent, if you like.  When you receive new messages relevant to a
+conversation, Gnorb will notice them and prompt you to associate them
+with the appropriate Org heading.  Attachments on incoming messages can
+be automatically saved as attachments on Org headings, using org-attach.
 
    In general, the goal is to keep track of whole conversations, reduce
 friction when moving between Gnus and Org, and keep you in the Org
 agenda rather than in Gnus.
 * Menu:
 
+* Basic Usage::
 * Email-Related Commands::
 * Trigger Actions::
 * Viewing Tracked Messages in *Summary* Buffers::
 * Hinting in Gnus::
 * Message Attachments::
+* Registry Usage::
 * Likely Workflow::
 
 
-File: gnorb.info,  Node: Email-Related Commands,  Next: Trigger Actions,  Up: 
Email Tracking
+File: gnorb.info,  Node: Basic Usage,  Next: Email-Related Commands,  Up: 
Email Tracking
+
+4.1 Basic Usage
+===============
+
+The following sections might be a bit confusing to read if you haven’t
+actually tried using Gnorb.  If you don’t want to dive in all the way
+just yet, you can just dabble your toes.  First set up email tracking as
+specified in *note Setup: Setup, then do the following:
+
+  1. Add “%ug” somewhere appropriate in your ‘gnus-summary-line-format’
+     variable.
+  2. If you don’t use a local archive method, add your sent message
+     groups to ‘gnorb-gnus-sent-groups’ (see the docstring).
+  3. Use Org capture from Gnus summary buffers to create reminders for
+     emails you need to reply to.
+  4. Reply to those emails by pressing “C-c t” on the TODO heading in
+     either the Agenda, or in regular Org files.
+  5. If you ever get confused about what’s associated with an Org
+     heading, press “C-c v” on the heading (works in either the Agenda,
+     or regular Org files).
+
+   That should be enough to get started.
 
-4.1 Email-Related Commands
+
+File: gnorb.info,  Node: Email-Related Commands,  Next: Trigger Actions,  
Prev: Basic Usage,  Up: Email Tracking
+
+4.2 Email-Related Commands
 ==========================
 
 Email tracking starts in one of three ways:
@@ -178,11 +207,14 @@ Email tracking starts in one of three ways:
   1. ‘gnorb-org-handle-mail’ is called on an Org heading to compose a
      new message.  By default, this will begin a reply to the most
      recent message in the conversation.  If there are no associated
-     messages to reply to (or you call the function with a double prefix
+     messages to reply to (or you call the function with a single prefix
      arg), Gnorb will look for mailto: or bbdb: links in the heading,
      and compose a new message to them.
 
-     The sent message will be associated with the Org heading, and
+     Calling the function with a double prefix arg will ignore all
+     associated messages and links, and compose a blank message.
+
+     Once sent, the message will be associated with the Org heading, and
      you’ll be brought back to the heading and asked to trigger an
      action on it.
 
@@ -190,7 +222,7 @@ Email tracking starts in one of three ways:
      ‘gnorb-org-handle-mail’.  It does the same thing as the latter, but
      first exports the body of the subtree as either text or a file,
      then inserts the text into the message body, or attaches the file
-     to the message, depending on what you’ve chosen.
+     to the message, respectively.
   2. ‘gnorb-gnus-incoming-do-todo’ is called on a message in a Gnus
      *Summary* buffer.  You’ll be prompted for an Org heading, taken to
      that heading, and asked to trigger an action on it.
@@ -218,39 +250,54 @@ Email tracking starts in one of three ways:
    Because these three commands all express a similar intent, but are
 called in different modes, it can make sense to give each of them the
 same keybinding in the keymaps for Org mode, Gnus summary mode, and
-Message mode, respectively.
+Message mode.
+
+   An additional convenience command is available for use in Gnus
+summary buffers: ‘gnorb-gnus-quick-reply’.  If you don’t want to go
+through the whole round trip of triggering an action and then starting a
+new reply, call this command on an incoming message to associate it with
+a heading, start a reply, and associate your reply with the same
+heading.
 
 
 File: gnorb.info,  Node: Trigger Actions,  Next: Viewing Tracked Messages in 
*Summary* Buffers,  Prev: Email-Related Commands,  Up: Email Tracking
 
-4.2 Trigger Actions
+4.3 Trigger Actions
 ===================
 
 After calling ‘gnorb-gnus-incoming-do-todo’ on a message, or after
 sending a message associated with an Org heading, you’ll be taken to the
 heading and asked to “trigger an action” on it.  At the moment there are
-four different possibilities: triggering a TODO state-change on the
+six different possibilities: triggering a TODO state-change on the
 heading, taking a note on the heading (both these options will associate
 the message with the heading), associating the message but doing nothing
-else, and lastly, doing nothing at all.
+else, capturing a new Org heading as a sibling to the tracked heading,
+capturing a new Org heading as a child, and lastly, doing nothing at
+all.
 
-   More actions will be added in the future; it’s also possible to
+   More actions may be added in the future; it’s also possible to
 rearrange or delete existing actions, and add your own: see the
 docstring of ‘gnorb-org-trigger-actions’.
 
 
 File: gnorb.info,  Node: Viewing Tracked Messages in *Summary* Buffers,  Next: 
Hinting in Gnus,  Prev: Trigger Actions,  Up: Email Tracking
 
-4.3 Viewing Tracked Messages in *Summary* Buffers
+4.4 Viewing Tracked Messages in *Summary* Buffers
 =================================================
 
-Call ‘gnorb-org-view’ on an Org heading to open an nnir *Summary* buffer
-showing all the messages associated with that heading (this requires
-that you’ve added an nngnorb server to your Gnus backends).  A minor
-mode will be in effect, ensuring that any replies you send to messages
-in this buffer will automatically be associated with the original Org
-heading.  You can also invoke ‘gnorb-summary-disassociate-message’ (“C-c
-d”) to disassociate the message with the Org heading.
+Call ‘gnorb-org-view’ on an Org heading to open an nnir summary buffer
+showing all the messages associated with that heading and child headings
+(this requires you to have added an nngnorb server to your Gnus
+backends).  A minor mode is in effect, ensuring that any replies you
+send to messages in this buffer will automatically be associated with
+the original Org heading.  You can also invoke
+‘gnorb-summary-disassociate-message’ (“C-c d”) to disassociate the
+message with the Org heading.
+
+   If you call ‘gnorb-org-view’ with a prefix argument, the search group
+will be made persistent across Gnus sessions.  You can re-run the search
+and update the group contents by hitting “M-g” on the group in the Gnus
+*Group* buffer.
 
    As a bonus, it’s possible to go into Gnus’ *Server* buffer, find the
 line specifying your nngnorb server, and hit “G” (aka
@@ -263,7 +310,7 @@ linked messages.  This is dog-slow at the moment; it will 
get faster.
 
 File: gnorb.info,  Node: Hinting in Gnus,  Next: Message Attachments,  Prev: 
Viewing Tracked Messages in *Summary* Buffers,  Up: Email Tracking
 
-4.4 Hinting in Gnus
+4.5 Hinting in Gnus
 ===================
 
 When you receive new mails that might be relevant to existing Org TODOs,
@@ -273,18 +320,19 @@ display a message in the minibuffer when opening 
potentially relevant
 messages.  You can then use ‘gnorb-gnus-incoming-to-todo’ to trigger an
 action on the relevant TODO.
 
-   This hinting can happen in the Gnus summary buffer as well.  If you
-use the escape indicated by ‘gnorb-gnus-summary-mark-format-letter” as
-part of your ‘gnus-summary-line-format’, articles that are relevant to
-TODOs will be marked with a special character in the Summary buffer, as
-determined by ‘gnorb-gnus-summary-mark’.  By default, the format letter
-is “g” (meaning it is used as “%ug” in the format line), and the mark is
-“¡”.
+   This hinting can happen in the Gnus summary buffer as well. If you
+use the escape indicated by ‘gnorb-gnus-summary-mark-format-letter’ as
+part of your ‘gnus-summary-line-format’, articles that may be relevant
+to TODOs will be marked with a special character in the Summary
+buffer, as determined by ‘gnorb-gnus-summary-mark’. By default, the
+format letter is “g” (meaning it is used as “%ug” in the format line),
+and the mark is “&” for messages that are already tracked, and “¡” for
+messages that may be relevant.
 
 
-File: gnorb.info,  Node: Message Attachments,  Next: Likely Workflow,  Prev: 
Hinting in Gnus,  Up: Email Tracking
+File: gnorb.info,  Node: Message Attachments,  Next: Registry Usage,  Prev: 
Hinting in Gnus,  Up: Email Tracking
 
-4.5 Message Attachments
+4.6 Message Attachments
 =======================
 
 Gnorb simplifies the handling of attachments that you receive in emails.
@@ -309,9 +357,21 @@ attach the files in the heading’s org-attach directory to 
the outgoing
 message.
 
 
-File: gnorb.info,  Node: Likely Workflow,  Prev: Message Attachments,  Up: 
Email Tracking
+File: gnorb.info,  Node: Registry Usage,  Next: Likely Workflow,  Prev: 
Message Attachments,  Up: Email Tracking
+
+4.7 Registry Usage
+==================
+
+You can see how many associations you’ve got stored in the registry by
+calling ‘gnorb-report-tracking-usage’.  This will pop up a buffer
+showing how much of the registry you’re using, and offering keybindings
+for ‘gnorb-flush-dead-associations’, to help Gnorb clean up after
+itself.
+
+
+File: gnorb.info,  Node: Likely Workflow,  Prev: Registry Usage,  Up: Email 
Tracking
 
-4.6 Likely Workflow
+4.8 Likely Workflow
 ===================
 
 You receive an email from Jimmy, who wants to rent a room in your house.
@@ -635,9 +695,13 @@ File: gnorb.info,  Node: User Optionsxx,  Prev: Viewing 
Org headlines relevant t
      relevant to Org TODOs.  Defaults to “g”, meaning it should be used
      as “%ug” in the format line.
 ‘`gnorb-gnus-summary-mark'’
-     The mark used to indicate relevant messages in the Summary buffer,
-     when ‘gnorb-gnus-summary-mark-format-letter’ is present in the
-     format line.  Defaults to “¡”.
+     The mark used to indicate potentially relevant messages in the
+     Summary buffer, when ‘gnorb-gnus-summary-mark-format-letter’ is
+     present in the format line.  Defaults to “¡”.
+‘`gnorb-gnus-summary-tracked-mark'’
+     The mark used to indicate already-tracked messages in the Summary
+     buffer, when ‘gnorb-gnus-summary-mark-format-letter’ is present in
+     the format line.  Defaults to “&”.
 
 
 File: gnorb.info,  Node: Suggested Keybindings,  Prev: Misc Gnus,  Up: Top
@@ -662,8 +726,9 @@ File: gnorb.info,  Node: Suggested Keybindings,  Prev: Misc 
Gnus,  Up: Top
           (org-defkey org-mode-map (kbd "C-c V") 'gnorb-org-popup-bbdb)
           (setq gnorb-org-agenda-popup-bbdb t)
           (eval-after-load "org-agenda"
-            '(progn (org-defkey org-agenda-mode-map (kbd "H") 
'gnorb-org-handle-mail)
-                    (org-defkey org-agenda-mode-map (kbd "V") 
'gnorb-org-popup-bbdb)))))
+            '(progn (org-defkey org-agenda-mode-map (kbd "C-c t") 
'gnorb-org-handle-mail)
+                    (org-defkey org-agenda-mode-map (kbd "C-c v") 
'gnorb-org-popup-bbdb)
+                    (org-defkey org-agenda-mode-map (kbd "V") 
'gnorb-org-view)))))
 
      (eval-after-load "gnorb-gnus"
        '(progn
@@ -692,31 +757,33 @@ File: gnorb.info,  Node: Suggested Keybindings,  Prev: 
Misc Gnus,  Up: Top
 
 Tag Table:
 Node: Top194
-Node: Introduction1009
-Node: Installation2118
-Node: Setup2532
-Node: Email Tracking3899
-Node: Email-Related Commands5430
-Node: Trigger Actions8440
-Node: Viewing Tracked Messages in *Summary* Buffers9289
-Node: Hinting in Gnus10523
-Node: Message Attachments11531
-Node: Likely Workflow12713
-Node: Restoring Window Layout15518
-Node: Recent Mails From BBDB Contacts15882
-Node: BBDB posting styles16878
-Node: BBDB Org tagging17794
-Node: Misc BBDB18540
-Node: Searching for messages from BBDB contacts18753
-Node: Citing BBDB contacts19199
-Node: User Options19520
-Node: Misc Org21059
-Node: Inserting BBDB links21234
-Node: User Optionsx21489
-Node: Misc Gnus24226
-Node: Viewing Org headlines relevant to a message24439
-Node: User Optionsxx24754
-Node: Suggested Keybindings27518
+Node: Introduction1044
+Node: Installation2153
+Node: Setup2567
+Node: Email Tracking3934
+Node: Basic Usage5544
+Node: Email-Related Commands6617
+Node: Trigger Actions10112
+Node: Viewing Tracked Messages in *Summary* Buffers11064
+Node: Hinting in Gnus12551
+Node: Message Attachments13647
+Node: Registry Usage14828
+Node: Likely Workflow15279
+Node: Restoring Window Layout18079
+Node: Recent Mails From BBDB Contacts18443
+Node: BBDB posting styles19439
+Node: BBDB Org tagging20355
+Node: Misc BBDB21101
+Node: Searching for messages from BBDB contacts21314
+Node: Citing BBDB contacts21760
+Node: User Options22081
+Node: Misc Org23620
+Node: Inserting BBDB links23795
+Node: User Optionsx24050
+Node: Misc Gnus26787
+Node: Viewing Org headlines relevant to a message27000
+Node: User Optionsxx27315
+Node: Suggested Keybindings30322
 
 End Tag Table
 
diff --git a/gnorb.org b/gnorb.org
index e19422d..58b0365 100644
--- a/gnorb.org
+++ b/gnorb.org
@@ -35,6 +35,9 @@ https://github.com/girzel/gnorb, and put the "gnorb" 
directory on your
 load-path. The Github site is also a good place to report bugs and
 other issues.
 * Setup
+:PROPERTIES:
+:ID:       9da59609-bb3c-4970-88f6-bddca18d2ad4
+:END:
 Loading "gnorb" will make the basic functions available. Using Gnorb
 for email tracking takes a bit more setup, however:
 
@@ -68,16 +71,36 @@ message IDs are associated with Org heading ids. As a 
conversation
 develops, messages are collected on a heading (and/or its children).
 You can compose new messages directly from the Org heading, and Gnorb
 will automatically associate your sent message with the conversation.
-You can open temporary Gnus *Summary* buffers holding all the messages
-associated with an Org subtree, and reply from there. When you receive
-new messages relevant to a conversation, Gnorb will notice them and
-prompt you to associate them with the appropriate Org heading.
-Attachments on incoming messages can be automatically saved as
-attachments on Org headings, using org-attach.
+You can open Gnus *Summary* buffers holding all the messages
+associated with an Org subtree, and reply from there -- these groups
+can be made persistent, if you like. When you receive new messages
+relevant to a conversation, Gnorb will notice them and prompt you to
+associate them with the appropriate Org heading. Attachments on
+incoming messages can be automatically saved as attachments on Org
+headings, using org-attach.
 
 In general, the goal is to keep track of whole conversations, reduce
 friction when moving between Gnus and Org, and keep you in the Org
 agenda rather than in Gnus.
+** Basic Usage
+The following sections might be a bit confusing to read if you haven't
+actually tried using Gnorb. If you don't want to dive in all the way
+just yet, you can just dabble your toes. First set up email tracking
+as specified in [[id:9da59609-bb3c-4970-88f6-bddca18d2ad4][Setup]], then do 
the following:
+
+1. Add "%ug" somewhere appropriate in your `gnus-summary-line-format'
+   variable.
+2. If you don't use a local archive method, add your sent message
+   groups to `gnorb-gnus-sent-groups' (see the docstring).
+3. Use Org capture from Gnus summary buffers to create reminders for
+   emails you need to reply to.
+4. Reply to those emails by pressing "C-c t" on the TODO heading in
+   either the Agenda, or in regular Org files.
+5. If you ever get confused about what's associated with an Org
+   heading, press "C-c v" on the heading (works in either the Agenda,
+   or regular Org files).
+
+That should be enough to get started.
 ** Email-Related Commands
 Email tracking starts in one of three ways:
 
@@ -94,11 +117,14 @@ There are three main email-related commands:
 1. `gnorb-org-handle-mail' is called on an Org heading to compose a
    new message. By default, this will begin a reply to the most recent
    message in the conversation. If there are no associated messages to
-   reply to (or you call the function with a double prefix arg), Gnorb
+   reply to (or you call the function with a single prefix arg), Gnorb
    will look for mailto: or bbdb: links in the heading, and compose a
    new message to them.
+
+   Calling the function with a double prefix arg will ignore all
+   associated messages and links, and compose a blank message.
    
-   The sent message will be associated with the Org heading, and
+   Once sent, the message will be associated with the Org heading, and
    you'll be brought back to the heading and asked to trigger an
    action on it.
    
@@ -106,7 +132,7 @@ There are three main email-related commands:
    `gnorb-org-handle-mail'. It does the same thing as the latter, but
    first exports the body of the subtree as either text or a file,
    then inserts the text into the message body, or attaches the file
-   to the message, depending on what you've chosen.
+   to the message, respectively.
 2. `gnorb-gnus-incoming-do-todo' is called on a message in a Gnus
    *Summary* buffer. You'll be prompted for an Org heading, taken to
    that heading, and asked to trigger an action on it.
@@ -134,31 +160,45 @@ There are three main email-related commands:
 Because these three commands all express a similar intent, but are
 called in different modes, it can make sense to give each of them the
 same keybinding in the keymaps for Org mode, Gnus summary mode, and
-Message mode, respectively.
+Message mode.
+
+An additional convenience command is available for use in Gnus summary
+buffers: `gnorb-gnus-quick-reply'. If you don't want to go through the
+whole round trip of triggering an action and then starting a new
+reply, call this command on an incoming message to associate it with a
+heading, start a reply, and associate your reply with the same
+heading.
 ** Trigger Actions
 After calling `gnorb-gnus-incoming-do-todo' on a message, or after
 sending a message associated with an Org heading, you'll be taken to
 the heading and asked to "trigger an action" on it. At the moment
-there are four different possibilities: triggering a TODO state-change
+there are six different possibilities: triggering a TODO state-change
 on the heading, taking a note on the heading (both these options will
 associate the message with the heading), associating the message but
-doing nothing else, and lastly, doing nothing at all.
+doing nothing else, capturing a new Org heading as a sibling to the
+tracked heading, capturing a new Org heading as a child, and lastly,
+doing nothing at all.
 
-More actions will be added in the future; it's also possible to
+More actions may be added in the future; it's also possible to
 rearrange or delete existing actions, and add your own: see the
 docstring of `gnorb-org-trigger-actions'.
 ** Viewing Tracked Messages in *Summary* Buffers
 :PROPERTIES:
 :END:
-Call `gnorb-org-view' on an Org heading to open an nnir *Summary*
-buffer showing all the messages associated with that heading (this
-requires that you've added an nngnorb server to your Gnus backends). A
-minor mode will be in effect, ensuring that any replies you send to
-messages in this buffer will automatically be associated with the
-original Org heading. You can also invoke
+Call `gnorb-org-view' on an Org heading to open an nnir summary buffer
+showing all the messages associated with that heading and child
+headings (this requires you to have added an nngnorb server to your
+Gnus backends). A minor mode is in effect, ensuring that any replies
+you send to messages in this buffer will automatically be associated
+with the original Org heading. You can also invoke
 `gnorb-summary-disassociate-message' ("C-c d") to disassociate the
 message with the Org heading.
 
+If you call `gnorb-org-view' with a prefix argument, the search group
+will be made persistent across Gnus sessions. You can re-run the
+search and update the group contents by hitting "M-g" on the group in
+the Gnus *Group* buffer.
+
 As a bonus, it's possible to go into Gnus' *Server* buffer, find the
 line specifying your nngnorb server, and hit "G" (aka
 `gnus-group-make-nnir-group'). At the query prompt, enter an Org-style
@@ -179,11 +219,12 @@ action on the relevant TODO.
 
 This hinting can happen in the Gnus summary buffer as well. If you use
 the escape indicated by `gnorb-gnus-summary-mark-format-letter" as
-part of your `gnus-summary-line-format', articles that are relevant to
-TODOs will be marked with a special character in the Summary buffer,
-as determined by `gnorb-gnus-summary-mark'. By default, the format
-letter is "g" (meaning it is used as "%ug" in the format line), and
-the mark is "¡".
+part of your `gnus-summary-line-format', articles that may be relevant
+to TODOs will be marked with a special character in the Summary
+buffer, as determined by `gnorb-gnus-summary-mark'. By default, the
+format letter is "g" (meaning it is used as "%ug" in the format line),
+and the mark is "&" for messages that are already tracked, and "¡" for
+messages that may be relevant.
 ** Message Attachments
 :PROPERTIES:
 :END:
@@ -207,6 +248,12 @@ The same process works in reverse: when you send a message 
from an Org
 heading using `gnorb-org-handle-mail', Gnorb will ask if you want to
 attach the files in the heading's org-attach directory to the outgoing
 message.
+** Registry Usage
+You can see how many associations you've got stored in the registry by
+calling `gnorb-report-tracking-usage'. This will pop up a buffer
+showing how much of the registry you're using, and offering
+keybindings for `gnorb-flush-dead-associations', to help Gnorb clean
+up after itself.
 ** Likely Workflow
 You receive an email from Jimmy, who wants to rent a room in your
 house. "I'll respond to this later," you think.
@@ -437,10 +484,14 @@ heading to jump to that heading.
      use as part of your `gnus-summary-line-format', to indicate
      messages which might be relevant to Org TODOs. Defaults to "g",
      meaning it should be used as "%ug" in the format line.
-- `gnorb-gnus-summary-mark' :: The mark used to indicate relevant
-     messages in the Summary buffer, when
+- `gnorb-gnus-summary-mark' :: The mark used to indicate potentially
+     relevant messages in the Summary buffer, when
      `gnorb-gnus-summary-mark-format-letter' is present in the format
      line. Defaults to "¡".
+- `gnorb-gnus-summary-tracked-mark' :: The mark used to indicate
+     already-tracked messages in the Summary buffer, when
+     `gnorb-gnus-summary-mark-format-letter' is present in the format
+     line. Defaults to "&".
 * Suggested Keybindings
 :PROPERTIES:
 :ID:       de1b2579-86c2-4bb1-b77e-3467a3d2b3c7
@@ -463,8 +514,9 @@ heading to jump to that heading.
        (org-defkey org-mode-map (kbd "C-c V") 'gnorb-org-popup-bbdb)
        (setq gnorb-org-agenda-popup-bbdb t)
        (eval-after-load "org-agenda"
-         '(progn (org-defkey org-agenda-mode-map (kbd "H") 
'gnorb-org-handle-mail)
-                 (org-defkey org-agenda-mode-map (kbd "V") 
'gnorb-org-popup-bbdb)))))
+         '(progn (org-defkey org-agenda-mode-map (kbd "C-c t") 
'gnorb-org-handle-mail)
+                 (org-defkey org-agenda-mode-map (kbd "C-c v") 
'gnorb-org-popup-bbdb)
+                 (org-defkey org-agenda-mode-map (kbd "V") 'gnorb-org-view)))))
 
   (eval-after-load "gnorb-gnus"
     '(progn
diff --git a/gnorb.texi b/gnorb.texi
index f1fccc5..cdd7576 100644
--- a/gnorb.texi
+++ b/gnorb.texi
@@ -42,11 +42,13 @@
 
 Email Tracking
 
+* Basic Usage::
 * Email-Related Commands::
 * Trigger Actions::
 * Viewing Tracked Messages in *Summary* Buffers::
 * Hinting in Gnus::
 * Message Attachments::
+* Registry Usage::
 * Likely Workflow::
 
 Misc BBDB
@@ -146,25 +148,57 @@ message IDs are associated with Org heading ids. As a 
conversation
 develops, messages are collected on a heading (and/or its children).
 You can compose new messages directly from the Org heading, and Gnorb
 will automatically associate your sent message with the conversation.
-You can open temporary Gnus *Summary* buffers holding all the messages
-associated with an Org subtree, and reply from there. When you receive
-new messages relevant to a conversation, Gnorb will notice them and
-prompt you to associate them with the appropriate Org heading.
-Attachments on incoming messages can be automatically saved as
-attachments on Org headings, using org-attach.
+You can open Gnus *Summary* buffers holding all the messages
+associated with an Org subtree, and reply from there -- these groups
+can be made persistent, if you like. When you receive new messages
+relevant to a conversation, Gnorb will notice them and prompt you to
+associate them with the appropriate Org heading. Attachments on
+incoming messages can be automatically saved as attachments on Org
+headings, using org-attach.
 
 In general, the goal is to keep track of whole conversations, reduce
 friction when moving between Gnus and Org, and keep you in the Org
 agenda rather than in Gnus.
 @menu
+* Basic Usage::
 * Email-Related Commands::
 * Trigger Actions::
 * Viewing Tracked Messages in *Summary* Buffers::
 * Hinting in Gnus::
 * Message Attachments::
+* Registry Usage::
 * Likely Workflow::
 @end menu
 
address@hidden Basic Usage
address@hidden Basic Usage
+
+The following sections might be a bit confusing to read if you haven't
+actually tried using Gnorb. If you don't want to dive in all the way
+just yet, you can just dabble your toes. First set up email tracking
+as specified in @ref{Setup,Setup}, then do the following:
+
address@hidden
address@hidden
+Add ``%ug'' somewhere appropriate in your `gnus-summary-line-format'
+variable.
address@hidden
+If you don't use a local archive method, add your sent message
+groups to `gnorb-gnus-sent-groups' (see the docstring).
address@hidden
+Use Org capture from Gnus summary buffers to create reminders for
+emails you need to reply to.
address@hidden
+Reply to those emails by pressing ``C-c t'' on the TODO heading in
+either the Agenda, or in regular Org files.
address@hidden
+If you ever get confused about what's associated with an Org
+heading, press ``C-c v'' on the heading (works in either the Agenda,
+or regular Org files).
address@hidden enumerate
+
+That should be enough to get started.
+
 @node Email-Related Commands
 @section Email-Related Commands
 
@@ -190,11 +224,14 @@ There are three main email-related commands:
 `gnorb-org-handle-mail' is called on an Org heading to compose a
 new message. By default, this will begin a reply to the most recent
 message in the conversation. If there are no associated messages to
-reply to (or you call the function with a double prefix arg), Gnorb
+reply to (or you call the function with a single prefix arg), Gnorb
 will look for mailto: or bbdb: links in the heading, and compose a
 new message to them.
 
-The sent message will be associated with the Org heading, and
+Calling the function with a double prefix arg will ignore all
+associated messages and links, and compose a blank message.
+
+Once sent, the message will be associated with the Org heading, and
 you'll be brought back to the heading and asked to trigger an
 action on it.
 
@@ -202,7 +239,7 @@ action on it.
 `gnorb-org-handle-mail'. It does the same thing as the latter, but
 first exports the body of the subtree as either text or a file,
 then inserts the text into the message body, or attaches the file
-to the message, depending on what you've chosen.
+to the message, respectively.
 @item
 `gnorb-gnus-incoming-do-todo' is called on a message in a Gnus
 *Summary* buffer. You'll be prompted for an Org heading, taken to
@@ -233,7 +270,14 @@ sent message for this purpose.
 Because these three commands all express a similar intent, but are
 called in different modes, it can make sense to give each of them the
 same keybinding in the keymaps for Org mode, Gnus summary mode, and
-Message mode, respectively.
+Message mode.
+
+An additional convenience command is available for use in Gnus summary
+buffers: `gnorb-gnus-quick-reply'. If you don't want to go through the
+whole round trip of triggering an action and then starting a new
+reply, call this command on an incoming message to associate it with a
+heading, start a reply, and associate your reply with the same
+heading.
 
 @node Trigger Actions
 @section Trigger Actions
@@ -241,27 +285,34 @@ Message mode, respectively.
 After calling `gnorb-gnus-incoming-do-todo' on a message, or after
 sending a message associated with an Org heading, you'll be taken to
 the heading and asked to ``trigger an action'' on it. At the moment
-there are four different possibilities: triggering a TODO state-change
+there are six different possibilities: triggering a TODO state-change
 on the heading, taking a note on the heading (both these options will
 associate the message with the heading), associating the message but
-doing nothing else, and lastly, doing nothing at all.
+doing nothing else, capturing a new Org heading as a sibling to the
+tracked heading, capturing a new Org heading as a child, and lastly,
+doing nothing at all.
 
-More actions will be added in the future; it's also possible to
+More actions may be added in the future; it's also possible to
 rearrange or delete existing actions, and add your own: see the
 docstring of `gnorb-org-trigger-actions'.
 
 @node Viewing Tracked Messages in *Summary* Buffers
 @section Viewing Tracked Messages in *Summary* Buffers
 
-Call `gnorb-org-view' on an Org heading to open an nnir *Summary*
-buffer showing all the messages associated with that heading (this
-requires that you've added an nngnorb server to your Gnus backends). A
-minor mode will be in effect, ensuring that any replies you send to
-messages in this buffer will automatically be associated with the
-original Org heading. You can also invoke
+Call `gnorb-org-view' on an Org heading to open an nnir summary buffer
+showing all the messages associated with that heading and child
+headings (this requires you to have added an nngnorb server to your
+Gnus backends). A minor mode is in effect, ensuring that any replies
+you send to messages in this buffer will automatically be associated
+with the original Org heading. You can also invoke
 `gnorb-summary-disassociate-message' (``C-c d'') to disassociate the
 message with the Org heading.
 
+If you call `gnorb-org-view' with a prefix argument, the search group
+will be made persistent across Gnus sessions. You can re-run the
+search and update the group contents by hitting ``M-g'' on the group in
+the Gnus *Group* buffer.
+
 As a bonus, it's possible to go into Gnus' *Server* buffer, find the
 line specifying your nngnorb server, and hit ``G'' (aka
 `gnus-group-make-nnir-group'). At the query prompt, enter an Org-style
@@ -281,12 +332,13 @@ messages. You can then use `gnorb-gnus-incoming-to-todo' 
to trigger an
 action on the relevant TODO.
 
 This hinting can happen in the Gnus summary buffer as well. If you use
-the escape indicated by `gnorb-gnus-summary-mark-format-letter'' as
-part of your `gnus-summary-line-format', articles that are relevant to
-TODOs will be marked with a special character in the Summary buffer,
-as determined by `gnorb-gnus-summary-mark'. By default, the format
-letter is ``g'' (meaning it is used as ``%ug'' in the format line), and
-the mark is ``¡''.
+the escape indicated by `gnorb-gnus-summary-mark-format-letter`` as
+part of your `gnus-summary-line-format', articles that may be relevant
+to TODOs will be marked with a special character in the Summary
+buffer, as determined by `gnorb-gnus-summary-mark'. By default, the
+format letter is ``g'' (meaning it is used as ``%ug'' in the format line),
+and the mark is ``&'' for messages that are already tracked, and ``¡'' for
+messages that may be relevant.
 
 @node Message Attachments
 @section Message Attachments
@@ -312,6 +364,15 @@ heading using `gnorb-org-handle-mail', Gnorb will ask if 
you want to
 attach the files in the heading's org-attach directory to the outgoing
 message.
 
address@hidden Registry Usage
address@hidden Registry Usage
+
+You can see how many associations you've got stored in the registry by
+calling `gnorb-report-tracking-usage'. This will pop up a buffer
+showing how much of the registry you're using, and offering
+keybindings for `gnorb-flush-dead-associations', to help Gnorb clean
+up after itself.
+
 @node Likely Workflow
 @section Likely Workflow
 
@@ -618,10 +679,15 @@ use as part of your `gnus-summary-line-format', to 
indicate
 messages which might be relevant to Org TODOs. Defaults to ``g'',
 meaning it should be used as ``%ug'' in the format line.
 @item `gnorb-gnus-summary-mark'
-The mark used to indicate relevant
-messages in the Summary buffer, when
+The mark used to indicate potentially
+relevant messages in the Summary buffer, when
 `gnorb-gnus-summary-mark-format-letter' is present in the format
 line. Defaults to ``¡''.
address@hidden `gnorb-gnus-summary-tracked-mark'
+The mark used to indicate
+already-tracked messages in the Summary buffer, when
+`gnorb-gnus-summary-mark-format-letter' is present in the format
+line. Defaults to ``&''.
 @end table
 
 @node Suggested Keybindings
@@ -645,8 +711,9 @@ line. Defaults to ``¡''.
      (org-defkey org-mode-map (kbd "C-c V") 'gnorb-org-popup-bbdb)
      (setq gnorb-org-agenda-popup-bbdb t)
      (eval-after-load "org-agenda"
-       '(progn (org-defkey org-agenda-mode-map (kbd "H") 
'gnorb-org-handle-mail)
-               (org-defkey org-agenda-mode-map (kbd "V") 
'gnorb-org-popup-bbdb)))))
+       '(progn (org-defkey org-agenda-mode-map (kbd "C-c t") 
'gnorb-org-handle-mail)
+               (org-defkey org-agenda-mode-map (kbd "C-c v") 
'gnorb-org-popup-bbdb)
+               (org-defkey org-agenda-mode-map (kbd "V") 'gnorb-org-view)))))
 
 (eval-after-load "gnorb-gnus"
   '(progn
@@ -672,5 +739,8 @@ line. Defaults to ``¡''.
      (define-key message-mode-map (kbd "C-c t") 'gnorb-gnus-outgoing-do-todo)))
 @end lisp
 
address@hidden Emacs 25.0.50.8 (Org mode 8.3beta)
address@hidden
\ No newline at end of file
address@hidden
address@hidden Local Variables:
address@hidden mode: texinfo
address@hidden TeX-master: t
address@hidden End:
diff --git a/nngnorb.el b/nngnorb.el
index eb55ee3..9d03e14 100644
--- a/nngnorb.el
+++ b/nngnorb.el
@@ -1,6 +1,6 @@
 ;;; nngnorb.el --- Gnorb backend for Gnus
 
-;; This file is in the public domain.
+;; Copyright (C) 2014  Free Software Foundation, Inc.
 
 ;; Author: Eric Abrahamsen <address@hidden>
 
@@ -52,7 +52,7 @@
 
 (make-variable-buffer-local 'nngnorb-attachment-file-list)
 
-(gnus-declare-backend "nngnorb" 'none)
+(gnus-declare-backend "nngnorb" 'post-mail 'virtual)
 
 (add-to-list 'nnir-method-default-engines '(nngnorb . gnorb))
 
@@ -79,14 +79,14 @@ be scanned for gnus messages, and those messages displayed."
   ;; a property, and the new registry-based system, we're going to use
   ;; both methods to collect relevant messages. This could be a little
   ;; slower, but for the time being it will be safer.
-  (save-excursion
+  (save-window-excursion
     (let ((q (cdr (assq 'query query)))
          (buf (get-buffer-create nnir-tmp-buffer))
          msg-ids org-ids links vectors)
       (with-current-buffer buf
        (erase-buffer)
        (setq nngnorb-attachment-file-list nil))
-      (when (equal "5.13" gnus-version-number)
+      (when (and (equal "5.13" gnus-version-number) (version< emacs-version 
"24.4"))
        (setq q (car q)))
       (cond ((string-match "id\\+\\([[:alnum:]-]+\\)$" q)
             (with-demoted-errors "Error: %S"
@@ -142,7 +142,7 @@ be scanned for gnus messages, and those messages displayed."
       (dolist (i (delq nil org-ids))
        (let ((rel-msg-id (gnorb-registry-org-id-search i)))
          (when rel-msg-id
-           (setq msg-ids (append rel-msg-id msg-ids)))))
+           (setq msg-ids (append (delq nil rel-msg-id) msg-ids)))))
       (when msg-ids
          (dolist (id msg-ids)
            (let ((link (gnorb-msg-id-to-link id)))
@@ -278,8 +278,12 @@ continue to provide tracking of sent messages."
        (message-insert-header
         (intern gnorb-mail-header)
         org-id)
-       (add-to-list 'message-exit-actions
-                    'gnorb-org-restore-after-send t))
+       ;; As with elsewhere, this should be redundant with
+       ;; `gnorb-gnus-check-outgoing-headers.'  Even if not, it
+       ;; should be switched to use `message-send-actions'
+       ;; (add-to-list 'message-exit-actions
+       ;; 'gnorb-org-restore-after-send t)
+       )
       (goto-char compose-marker))
     (when attachments
       (map-y-or-n-p
@@ -309,7 +313,7 @@ the message being included in this search."
      (gnus-summary-article-number)))
   (let* ((msg-id (gnus-fetch-original-field "message-id"))
         (org-ids (gnus-registry-get-id-key msg-id 'gnorb-ids))
-        chosen)
+        chosen multiple-alist)
     (if org-ids
        (progn
          (if (= (length org-ids) 1)
@@ -317,14 +321,18 @@ the message being included in this search."
              (progn (gnus-registry-set-id-key msg-id 'gnorb-ids nil)
                     (setq chosen (car org-ids)))
            ;; Multiple associated TODOs, prompt to choose one.
+           (setq multiple-alist
+                 (mapcar
+                  (lambda (h)
+                    (cons (gnorb-pretty-outline h) h))
+                  org-ids))
            (setq chosen
                  (cdr
-                  (org-completing-read
-                   "Choose a TODO to disassociate from: "
-                   (mapcar
-                    (lambda (h)
-                      (cons (gnorb-pretty-outline h) h))
-                    org-ids))))
+                  (assoc
+                   (org-completing-read
+                    "Choose a TODO to disassociate from: "
+                    multiple-alist)
+                   multiple-alist)))
            (gnus-registry-set-id-key msg-id 'gnorb-ids
                                      (remove chosen org-ids)))
          (message "Message disassociated from %s"



reply via email to

[Prev in Thread] Current Thread [Next in Thread]