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

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

[elpa] master 57629db 2/4: Merge commit '3e5c11a13981a1ff613cb4442ad6442


From: Eric Abrahamsen
Subject: [elpa] master 57629db 2/4: Merge commit '3e5c11a13981a1ff613cb4442ad644285c44e481' from gnorb
Date: Thu, 23 Apr 2015 09:17:48 +0000

branch: master
commit 57629db9a7dc542f17f553ce362dacd01db70279
Merge: c7a6156 3e5c11a
Author: Eric Abrahamsen <address@hidden>
Commit: Eric Abrahamsen <address@hidden>

    Merge commit '3e5c11a13981a1ff613cb4442ad644285c44e481' from gnorb
---
 packages/gnorb/README.org        |   29 ++++-
 packages/gnorb/gnorb-bbdb.el     |   37 ++---
 packages/gnorb/gnorb-gnus.el     |  288 ++++++++++++++++++++++++++------------
 packages/gnorb/gnorb-org.el      |   46 ++++---
 packages/gnorb/gnorb-registry.el |  132 ++++++++++++++---
 packages/gnorb/gnorb-utils.el    |  162 +++++++++++++++-------
 packages/gnorb/gnorb.el          |    8 +-
 packages/gnorb/gnorb.info        |  199 ++++++++++++++++++---------
 packages/gnorb/gnorb.org         |  108 +++++++++++----
 packages/gnorb/gnorb.texi        |  132 +++++++++++++----
 packages/gnorb/nngnorb.el        |   34 +++--
 11 files changed, 828 insertions(+), 347 deletions(-)

diff --git a/packages/gnorb/README.org b/packages/gnorb/README.org
index 1f8f82f..9e2f9bd 100644
--- a/packages/gnorb/README.org
+++ b/packages/gnorb/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/packages/gnorb/gnorb-bbdb.el b/packages/gnorb/gnorb-bbdb.el
index 572a4b9..eb2f6eb 100644
--- a/packages/gnorb/gnorb-bbdb.el
+++ b/packages/gnorb/gnorb-bbdb.el
@@ -150,8 +150,6 @@ be composed, just as in `gnus-posting-styles'.
 An example value might look like:"
   :group 'gnorb-bbdb)
 
-(defvar message-mode-hook)
-
 (when (fboundp 'bbdb-record-xfield-string)
   (fset (intern (format "bbdb-read-xfield-%s"
                        gnorb-bbdb-org-tag-field))
@@ -207,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
@@ -392,14 +392,16 @@ both, use \"C-u\" before the \"*\"."
         (mapconcat
          'identity
          (delete-dups
-          (cl-mapcan (lambda (r)
-                    (bbdb-record-xfield-split r gnorb-bbdb-org-tag-field))
-                  records))
+          (cl-mapcan
+           (lambda (r)
+             (bbdb-record-xfield-split r gnorb-bbdb-org-tag-field))
+           records))
          "|")))
     (if tag-string
        ;; C-u = all headings, not just todos
-       (org-tags-view (not (equal current-prefix-arg '(4)))
-                       tag-string)
+       (if (equal current-prefix-arg '(4))
+           (org-tags-view nil tag-string)
+         (org-tags-view t tag-string))
       (error "No org-tags field present"))))
 
 ;;;###autoload
@@ -424,9 +426,9 @@ a prefix arg and \"*\", the prefix arg must come first."
     (when (equal current-prefix-arg '(4))
       (setq search-string
            (read-from-minibuffer
-            (format "%s search string: " (cl-first backend)) search-string)))
+            (format "%s search string: " (first backend)) search-string)))
     (funcall (cl-third backend) search-string)
-    (delete-other-windows)))  
+    (delete-other-windows)))
 
 ;;;###autoload
 (defun gnorb-bbdb-cite-contact (rec)
@@ -437,9 +439,8 @@ a prefix arg and \"*\", the prefix arg must come first."
      mail-string)))
 
 ;;; Field containing links to recent messages
-
 (when (boundp 'bbdb-xfield-label-list)
-  (add-to-list 'bbdb-xfield-label-list gnorb-bbdb-messages-field nil 'eq))
+ (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/packages/gnorb/gnorb-gnus.el b/packages/gnorb/gnorb-gnus.el
index 2d3c5b0..a77a7ed 100644
--- a/packages/gnorb/gnorb-gnus.el
+++ b/packages/gnorb/gnorb-gnus.el
@@ -107,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
@@ -189,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)
@@ -233,18 +239,20 @@ 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 ()
-  (when (and org-note-abort
-            (org-capture-get :gnus-attachments))
-    ;; FIXME: Yuck: setting `abort-note' will fail as soon as org-capture.el is
-    ;; compiled with lexical-binding!
-    (condition-case nil
-       (progn (org-attach-delete-all)
-              (setq abort-note 'clean)
-              ;; remove any gnorb-mail-header values here
-              )
-      (error
-       (setq abort-note 'dirty)))))
+  (with-no-warnings ; For `org-note-abort'
+    (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)
+               ;; remove any gnorb-mail-header values here
+               )
+       (error
+       (setq abort-note 'dirty))))))
 
 (add-hook 'org-capture-prepare-finalize-hook
          'gnorb-gnus-capture-abort-cleanup)
@@ -297,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)
@@ -379,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)
@@ -422,12 +432,9 @@ 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"
@@ -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-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 &optional ret)
+(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-"
@@ -669,8 +771,8 @@ option `gnorb-gnus-hint-relevant-article' is non-nil."
   (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/packages/gnorb/gnorb-org.el b/packages/gnorb/gnorb-org.el
index 6d3772d..3392111 100644
--- a/packages/gnorb/gnorb-org.el
+++ b/packages/gnorb/gnorb-org.el
@@ -43,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
@@ -56,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
@@ -177,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
@@ -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))))
@@ -614,7 +618,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)))))
@@ -646,14 +652,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))
@@ -672,7 +681,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/packages/gnorb/gnorb-registry.el b/packages/gnorb/gnorb-registry.el
index f7b402c..bcd5adc 100644
--- a/packages/gnorb/gnorb-registry.el
+++ b/packages/gnorb/gnorb-registry.el
@@ -49,6 +49,7 @@
 ;;; Code:
 
 (require 'gnus-registry)
+(require 'gnorb-utils)
 (require 'cl-lib)
 
 (defgroup gnorb-registry nil
@@ -85,32 +86,27 @@ 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 ()
   (when (and (org-capture-get :gnorb-id)
             org-note-abort)
-    (condition-case nil
-       (let* ((msg-id (format "<%s>" (plist-get org-store-link-plist 
:message-id)))
-              (existing-org-ids (gnus-registry-get-id-key msg-id 'gnorb-ids))
-              (org-id (org-capture-get :gnorb-id)))
-         (when (member org-id existing-org-ids)
-           (gnus-registry-set-id-key msg-id 'gnorb-ids
-                                     (remove org-id existing-org-ids)))
-          ;; FIXME: Yuck!  This will fail as soon as org-capture.el is compiled
-          ;; with lexical-binding.
-         (setq abort-note 'clean))
-      (error
-       (setq abort-note 'dirty)))))
+    (with-no-warnings ; For `abort-note'
+      (condition-case error
+         (let* ((msg-id (format "<%s>" (plist-get org-store-link-plist 
:message-id)))
+                (existing-org-ids (gnus-registry-get-id-key msg-id 'gnorb-ids))
+                (org-id (org-capture-get :gnorb-id)))
+           (when (member org-id existing-org-ids)
+             (gnus-registry-set-id-key msg-id 'gnorb-ids
+                                       (remove org-id existing-org-ids)))
+           (setq abort-note 'clean))
+       (error
+        (setq abort-note 'dirty))))))
 
 (defun gnorb-find-visit-candidates (ids &optional include-zombies)
   "For all message-ids in IDS (which should be a list of
@@ -150,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.
 
diff --git a/packages/gnorb/gnorb-utils.el b/packages/gnorb/gnorb-utils.el
index 29185a1..d7f5e86 100644
--- a/packages/gnorb/gnorb-utils.el
+++ b/packages/gnorb/gnorb-utils.el
@@ -24,6 +24,8 @@
 
 ;;; Code:
 
+(require 'cl-lib)
+
 (require 'mailcap)
 (mailcap-parse-mimetypes)
 
@@ -72,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
@@ -211,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)
@@ -253,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.
@@ -383,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/packages/gnorb/gnorb.el b/packages/gnorb/gnorb.el
index 22531d2..0c9557e 100644
--- a/packages/gnorb/gnorb.el
+++ b/packages/gnorb/gnorb.el
@@ -32,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)
 ;;; gnorb.el ends here
diff --git a/packages/gnorb/gnorb.info b/packages/gnorb/gnorb.info
index 08085e1..52ca072 100644
--- a/packages/gnorb/gnorb.info
+++ b/packages/gnorb/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/packages/gnorb/gnorb.org b/packages/gnorb/gnorb.org
index e19422d..58b0365 100644
--- a/packages/gnorb/gnorb.org
+++ b/packages/gnorb/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/packages/gnorb/gnorb.texi b/packages/gnorb/gnorb.texi
index f1fccc5..cdd7576 100644
--- a/packages/gnorb/gnorb.texi
+++ b/packages/gnorb/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/packages/gnorb/nngnorb.el b/packages/gnorb/nngnorb.el
index d1ed896..9d03e14 100644
--- a/packages/gnorb/nngnorb.el
+++ b/packages/gnorb/nngnorb.el
@@ -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]