emacs-diffs
[Top][All Lists]
Advanced

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

master ecfc13e: Introduce nnselect backend for gnus


From: Andrew G Cohen
Subject: master ecfc13e: Introduce nnselect backend for gnus
Date: Fri, 4 Sep 2020 20:50:00 -0400 (EDT)

branch: master
commit ecfc13e41627511769e00a2d0a7568d5bdbe8a0d
Author: Andrew G Cohen <cohen@andy.bu.edu>
Commit: Andrew G Cohen <cohen@andy.bu.edu>

    Introduce nnselect backend for gnus
    
    This new backend allows gnus to handle arbitrary sets of messages
    spanning multiple groups, even when these groups are from different
    backends and different servers. All gnus glue is removed from
    nnir (leaving only the backend search functions) and gnus
    search-related processing is done through nnselect. In appropriate
    places 'nnir' has been replaced by 'nnselect' or 'search'.
    
    * etc/NEWS: Document the change.
    * doc/misc/gnus.texi: New  documentation for nnselect and update
    searching and thread-referral  sections.
    * lisp/gnus/nnselect.el: New file.
    * lisp/gnus/nnir.el: Remove all gnus glue, leaving only searching
    capability. Improve documentation strings.
    * lisp/gnus/gnus-group.el (gnus-group-read-ephemeral-search-group,
    gnus-group-make-search-group): New functions.
    * lisp/gnus/gnus-msg.el (gnus-setup-message, gnus-group-news,
    gnus-summary-news-other-window): Update to work for nnselect. Fix
    gnus-newsgroup-name wrangling.
    *lisp/gnus/gnus-registry.el
    (gnus-registry-action,gnus-registry-ignore-group-p): Make work from 
nnselect.
    * lisp/gnus/nnheader.el (nnheader-parse-head, nnheader-parse-nov):
    Rework and consolidate header parsing.
    * lisp/gnus/gnus-agent.el (gnus-agent-regenerate-group):
    * lisp/gnus/gnus-cache.el (gnus-possibly-enter-article):
    * lisp/gnus/gnus-cloud.el (gnus-cloud-available-chunks):
    * lisp/gnus/gnus-msg.el (gnus-inews-yank-articles):
    * lisp/gnus/gnus-sum. (gnus-get-newsgroup-headers):
    * lisp/gnus/nndiary.el (nndiary-parse-head):
    * lisp/gnus/nnfolder.el (nnfolder-parse-head):
    * lisp/gnus/nnmaildir.el (nnmaildir--update-nov):
    * lisp/gnus/nnml.el (nnml-parse-head):
    * lisp/gnus/nnspool.el (nnspool-insert-nov-head):
    Use new header parsing.
    * lisp/gnus/gnus-start.el (gnus-read-active-for-groups): Rescan on
    activation by default.
    * lisp/gnus/gnus-sum.el (gnus-summary-line-format-alist): New specs
    for virtual groups.
    (gnus-article-sort-by-rsv, gnus-thread-sort-by-rsv):  New functions to
    allow sorting by search RSV.
---
 doc/misc/gnus.texi         | 259 ++++++++++----
 etc/NEWS                   |  19 +-
 lisp/gnus/gnus-agent.el    |   2 +-
 lisp/gnus/gnus-cache.el    |   2 +-
 lisp/gnus/gnus-cloud.el    |  10 +-
 lisp/gnus/gnus-group.el    |  54 ++-
 lisp/gnus/gnus-msg.el      | 120 +++----
 lisp/gnus/gnus-registry.el |  13 +-
 lisp/gnus/gnus-srvr.el     |   5 +-
 lisp/gnus/gnus-start.el    |   2 +-
 lisp/gnus/gnus-sum.el      | 295 ++++------------
 lisp/gnus/gnus.el          |   7 +-
 lisp/gnus/nndiary.el       |   2 +-
 lisp/gnus/nnfolder.el      |   2 +-
 lisp/gnus/nnheader.el      | 344 ++++++++++--------
 lisp/gnus/nnimap.el        |  10 +-
 lisp/gnus/nnir.el          | 857 +++++++++-----------------------------------
 lisp/gnus/nnmaildir.el     |   2 +-
 lisp/gnus/nnml.el          |   2 +-
 lisp/gnus/nnselect.el      | 864 +++++++++++++++++++++++++++++++++++++++++++++
 lisp/gnus/nnspool.el       |   2 +-
 21 files changed, 1651 insertions(+), 1222 deletions(-)

diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index 0bdc2fa..593f113 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -641,7 +641,7 @@ Select Methods
 * Getting Mail::                Reading your personal mail with Gnus.
 * Browsing the Web::            Getting messages from a plethora of Web 
sources.
 * Other Sources::               Reading directories, files.
-* Combined Groups::             Combining groups into one group.
+* Virtual Groups::              Combining articles from multiple sources.
 * Email Based Diary::           Using mails to manage diary events in Gnus.
 * Gnus Unplugged::              Reading news and mail offline.
 
@@ -716,9 +716,10 @@ Document Groups
 
 * Document Server Internals::   How to add your own document types.
 
-Combined Groups
+Virtual Groups
 
-* Virtual Groups::              Combining articles from many groups.
+* Selection Groups::            Articles selected from many places.
+* Combined Groups::             Combining multiple  groups.
 
 Email Based Diary
 
@@ -10407,12 +10408,20 @@ article (@code{gnus-summary-refer-references}).
 @findex gnus-summary-refer-thread
 @kindex A T @r{(Summary)}
 Display the full thread where the current article appears
-(@code{gnus-summary-refer-thread}).  This command has to fetch all the
-headers in the current group to work, so it usually takes a while.  If
-you do it often, you may consider setting @code{gnus-fetch-old-headers}
-to @code{invisible} (@pxref{Filling In Threads}).  This won't have any
-visible effects normally, but it'll make this command work a whole lot
-faster.  Of course, it'll make group entry somewhat slow.
+(@code{gnus-summary-refer-thread}). By default this command looks for
+articles only in the current group. Some backends (currently only
+'nnimap) know how to find articles in the thread directly. In other
+cases each header in the current group must be fetched and examined,
+so it usually takes a while.  If you do it often, you may consider
+setting @code{gnus-fetch-old-headers} to @code{invisible}
+(@pxref{Filling In Threads}).  This won't have any visible effects
+normally, but it'll make this command work a whole lot faster.  Of
+course, it'll make group entry somewhat slow.
+
+@vindex gnus-refer-thread-use-search
+If @code{gnus-refer-thread-use-search} is non-nil then those backends
+that know how to find threads directly will search not just in the
+current group but all groups on the same server.
 
 @vindex gnus-refer-thread-limit
 The @code{gnus-refer-thread-limit} variable says how many old (i.e.,
@@ -10421,6 +10430,15 @@ fetch when doing this command.  The default is 200.  
If @code{t}, all
 the available headers will be fetched.  This variable can be overridden
 by giving the @kbd{A T} command a numerical prefix.
 
+@vindex gnus-refer-thread-limit-to-thread
+In most cases @code{gnus-refer-thread} adds any articles it finds to
+the current summary buffer. (When @code{gnus-refer-thread-use-search}
+is true and the initial referral starts from a summary buffer for a
+non-virtual group this may not be possible. In this case a new summary
+buffer is created holding a virtual group with the result of the thread
+search). If @code{gnus-refer-thread-limit-to-thread} is non-nil then
+the summary buffer will be limited to articles in the thread.
+
 @item M-^ (Summary)
 @findex gnus-summary-refer-article
 @kindex M-^ @r{(Summary)}
@@ -13262,7 +13280,7 @@ The different methods all have their peculiarities, of 
course.
 * Getting Mail::                Reading your personal mail with Gnus.
 * Browsing the Web::            Getting messages from a plethora of Web 
sources.
 * Other Sources::               Reading directories, files.
-* Combined Groups::             Combining groups into one group.
+* Virtual Groups::              Combining articles and groups together.
 * Email Based Diary::           Using mails to manage diary events in Gnus.
 * Gnus Unplugged::              Reading news and mail offline.
 @end menu
@@ -17834,19 +17852,133 @@ methods, but want to only use secondary ones:
 @end lisp
 
 
-@node Combined Groups
-@section Combined Groups
+@node Virtual Groups
+@section Virtual Groups
 
-Gnus allows combining a mixture of all the other group types into bigger
-groups.
+Gnus allows combining articles from many sources, and combinations of
+whole groups together into virtual groups.
 
 @menu
-* Virtual Groups::              Combining articles from many groups.
+* Selection Groups::            Combining articles from many groups.
+* Combined Groups::             Combining  multiple  groups.
 @end menu
 
 
-@node Virtual Groups
-@subsection Virtual Groups
+@node Selection Groups
+@subsection Select Groups
+@cindex nnselect
+@cindex select groups
+@cindex selecting articles
+
+
+Gnus provides the @dfn{nnselect} method for creating virtual groups
+composed of collections of messages, even when these messages come
+from groups that span multiple servers and backends. For the most part
+these virtual groups behave like any other group: messages may be
+threaded, marked, moved, deleted, copied, etc.; groups may be
+ephemeral or persistent; groups may be created via
+@code{gnus-group-make-group} or browsed as foreign via
+@code{gnus-group-browse-foreign-server}.
+
+The key to using an nnselect group is specifying the messages to
+include. Each nnselect group has a group parameter
+@code{nnselect-specs} which is an alist with two elements: a function
+@code{nnselect-function}; and arguments @code{nnselect-args} to be
+passed to the function, if any.
+
+The function @code{nnselect-function} must return a vector. Each
+element of this vector is in turn a 3-element vector corresponding to
+one message. The 3 elements are: the fully-qualified group name; the
+message number; and a "score" that can be used for additional
+sorting. The values for the score are arbitrary, and are not used
+directly by the nnselect method---they may, for example, all be set to
+100.
+
+Here is an example:
+
+@lisp
+ (nnselect-specs
+  (nnselect-function . identity)
+  (nnselect-args .
+        [["nnimap+work:mail" 595 100]
+         ["nnimap+home:sent" 223 100]
+          ["nntp+news.gmane.org:gmane.emacs.gnus.general" 23666 100]]))
+@end lisp
+
+The function is the identity and the argument is just the list of
+messages to include in the virtual group.
+
+Or we may wish to create a group from the results of a search query:
+
+@lisp
+ (nnselect-specs
+  (nnselect-function . nnir-run-query)
+  (nnselect-args
+   (nnir-query-spec
+    (query . "FLAGGED")
+    (criteria . ""))
+   (nnir-group-spec
+    ("nnimap:home")
+    ("nnimap:work"))))
+@end lisp
+
+This creates a group including all flagged messages from all groups on
+two imap servers, "home" and "work".
+
+And one last example. Here is a function that runs a search query to
+find all message that have been received recently from certain groups:
+
+@lisp
+(defun my-recent-email (args)
+  (let ((query-spec
+         (list
+          (cons 'query
+                (format-time-string "SENTSINCE %d-%b-%Y"
+                                    (time-subtract (current-time)
+                                                   (days-to-time (car args)))))
+          (cons 'criteria "")))
+        (group-spec  (cadr args)))
+    (nnir-run-query   (cons 'nnir-specs
+                            (list (cons 'nnir-query-spec query-spec)
+                                  (cons 'nnir-group-spec group-spec))))))
+@end lisp
+
+Then an nnselect-specs
+
+@lisp
+ (nnselect-specs
+  (nnselect-function . my-recent-email)
+  (nnselect-args . (7 (("nnimap:home") ("nnimap:work")))))
+@end lisp
+
+will provide a group composed of all messages on the home and work
+servers received in the last 7 days.
+
+Refreshing the selection of an nnselect group by running the
+@code{nnselect-function} may take a long time to
+complete. Consequently nnselect groups are not refreshed by default
+when @code{gnus-group-get-new-news} is invoked. In those cases where
+running the function is not too time-consuming, a non-nil group
+parameter of @code{nnselect-rescan} will allow automatic refreshing. A
+refresh can always be invoked manually through
+@code{gnus-group-get-new-news-this-group}.
+
+The nnir interface (@pxref{nnir}) includes engines for searching a
+variety of backends. While the details of each search engine vary, the
+result of an nnir search is always a vector of the sort used by the
+nnselect method, and the results of nnir queries are usually viewed
+using an nnselect group. Indeed the standard search function
+@code{gnus-group-read-ephemeral-search-group} just creates an
+ephemeral nnselect group with the appropriate nnir query as the
+@code{nnselect-specs}.  nnir originally included both the search
+engines and the glue to connect search results to gnus. Over time this
+glue evolved into the nnselect method. The two had
+a mostly amicable parting so that nnselect could pursue its dream of
+becoming a fully functioning backend, but occasional conflicts may
+still linger.
+
+@node Combined Groups
+@subsection Combined Groups
 @cindex nnvirtual
 @cindex virtual groups
 @cindex merging groups
@@ -21238,14 +21370,26 @@ four days, Gnus will decay the scores four times, for 
instance.
 @chapter Searching
 @cindex searching
 
-FIXME: Add a brief overview of Gnus search capabilities.  A brief
-comparison of nnir, nnmairix, contrib/gnus-namazu would be nice
-as well.
-
-This chapter describes tools for searching groups and servers for
-articles matching a query and then retrieving those articles.  Gnus
-provides a simpler mechanism for searching through articles in a summary buffer
-to find those matching a pattern. @xref{Searching for Articles}.
+FIXME: A brief comparison of nnir, nnmairix, contrib/gnus-namazu would
+be nice.
+
+Gnus has various ways of finding articles that match certain criteria
+(from a particular author, on a certain subject, etc). The simplest
+method is to enter a group and then either "limit" the summary buffer
+to the desired articles using the limiting commands (@xref{Limiting}),
+or searching through messages in the summary buffer (@xref{Searching
+for Articles}).
+
+Limiting commands and summary buffer searching work on subsets of the
+articles already fetched from the servers, and these commands won’t
+query the server for additional articles.  While simple, these methods
+are therefore inadequate if the desired articles span multiple groups,
+or if the group is so large that fetching all articles is
+impractical. Many backends (such as imap, notmuch, namazu, etc.)
+provide their own facilities to search for articles directly on the
+server and gnus can take advantage of these methods.  This chapter
+describes tools for searching groups and servers for articles matching
+a query.
 
 @menu
 * nnir::                     Searching with various engines.
@@ -21275,7 +21419,7 @@ through mail and news repositories.  Different backends 
(like
 interface.
 
 The @code{nnimap} search engine should work with no configuration.
-Other engines require a local index that needs to be created and
+Other engines may require a local index that needs to be created and
 maintained outside of Gnus.
 
 
@@ -21283,23 +21427,19 @@ maintained outside of Gnus.
 @subsection Basic Usage
 
 In the group buffer typing @kbd{G G} will search the group on the
-current line by calling @code{gnus-group-make-nnir-group}.  This prompts
-for a query string, creates an ephemeral @code{nnir} group containing
+current line by calling @code{gnus-group-make-search-group}.  This prompts
+for a query string, creates an ephemeral @code{nnselect} group containing
 the articles that match this query, and takes you to a summary buffer
 showing these articles.  Articles may then be read, moved and deleted
 using the usual commands.
 
-The @code{nnir} group made in this way is an @code{ephemeral} group,
-and some changes are not permanent: aside from reading, moving, and
-deleting, you can't act on the original article.  But there is an
-alternative: you can @emph{warp} (i.e., jump) to the original group
-for the article on the current line with @kbd{A W}, aka
-@code{gnus-warp-to-article}.  Even better, the function
-@code{gnus-summary-refer-thread}, bound by default in summary buffers
-to @kbd{A T}, will first warp to the original group before it works
-its magic and includes all the articles in the thread.  From here you
-can read, move and delete articles, but also copy them, alter article
-marks, whatever.  Go nuts.
+The @code{nnselect} group made in this way is an @code{ephemeral}
+group, and will disappear upon exit from the group.  However changes
+made in the group are permanently reflected in the real groups from
+which the articles are drawn.  It is occasionally convenient to view
+articles found through searching in their original group. You can
+@emph{warp} (i.e., jump) to the original group for the article on the
+current line with @kbd{A W}, aka @code{gnus-warp-to-article}.
 
 You say you want to search more than just the group on the current line?
 No problem: just process-mark the groups you want to search.  You want
@@ -21307,14 +21447,14 @@ even more?  Calling for an nnir search with the 
cursor on a topic heading
 will search all the groups under that heading.
 
 Still not enough?  OK, in the server buffer
-@code{gnus-group-make-nnir-group} (now bound to @kbd{G}) will search all
-groups from the server on the current line.  Too much?  Want to ignore
-certain groups when searching, like spam groups?  Just customize
-@code{nnir-ignored-newsgroups}.
+@code{gnus-group-make-search-group} (now bound to @kbd{G}) will search
+all groups from the server on the current line.  Too much?  Want to
+ignore certain groups when searching, like spam groups?  Just
+customize @code{nnir-ignored-newsgroups}.
 
 One more thing: individual search engines may have special search
 features.  You can access these special features by giving a prefix-arg
-to @code{gnus-group-make-nnir-group}.  If you are searching multiple
+to @code{gnus-group-make-search-group}.  If you are searching multiple
 groups with different search engines you will be prompted for the
 special search features for each engine separately.
 
@@ -21371,8 +21511,7 @@ variable is set to use the @code{imap} engine for all 
servers using the
 your servers with an @code{nnimap} backend you could change this to
 
 @lisp
-'((nnimap . namazu)
-  (nntp . gmane))
+'((nnimap . namazu))
 @end lisp
 
 @node The imap Engine
@@ -21575,7 +21714,7 @@ This engine is obsolete.
 
 @item nnir-method-default-engines
 Alist of pairs of server backends and search engines.  The default
-associations are
+association is
 @example
 (nnimap . imap)
 @end example
@@ -21584,32 +21723,6 @@ associations are
 A regexp to match newsgroups in the active file that should be skipped
 when searching all groups on a server.
 
-@item nnir-summary-line-format
-The format specification to be used for lines in an nnir summary buffer.
-All the items from @code{gnus-summary-line-format} are available, along with
-three items unique to nnir summary buffers:
-
-@example
-%Z    Search retrieval score value (integer)
-%G    Article original full group name (string)
-%g    Article original short group name (string)
-@end example
-
-If @code{nil} (the default) this will use @code{gnus-summary-line-format}.
-
-@item nnir-retrieve-headers-override-function
-If non-@code{nil}, a function that retrieves article headers rather than using
-the gnus built-in function.  This function takes an article list and
-group as arguments and populates the @code{nntp-server-buffer} with the
-retrieved headers.  It should then return either 'nov or 'headers
-indicating the retrieved header format.  Failure to retrieve headers
-should return @code{nil}.
-
-If this variable is @code{nil}, or if the provided function returns
-@code{nil} for a search result, @code{gnus-retrieve-headers} will be
-called instead."
-
-
 @end table
 
 
diff --git a/etc/NEWS b/etc/NEWS
index e0ea8f5..da3928d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -317,7 +317,24 @@ tags to be considered as well.
 ** Gnus
 
 +++
-*** New user option 'gnus-dbus-close-on-sleep'.
+*** New backend 'nnselect'
+The newly added nnselect backend allows creating groups from an
+arbitrary list of articles that may come from multiple groups and
+servers. These groups generally behave like any other group: they may
+be ephemeral or persistent, and allow article marking, moving,
+deletion, etc. Nnselect groups may be created like any other group,
+but there is also a convenience function for the common case of
+obtaining the list of articles as a result of a search:
+'gnus-group-make-search-group' (G g) that will prompt for an nnir
+search query and create a dedicated group for that search.  As part of
+this addition, the variable 'nnir-summary-line-format' has been
+removed; it's functionality is now available directly in the
+'gnus-summary-line-format' 'G' and 'g' specs. The variable
+'gnus-refer-thread-use-nnir' has been renamed
+'gnus-refer-thread-use-search'.
+
++++
+*** New user option 'gnus-dbus-close-on-sleep'
 On systems with D-Bus support, it is now possible to register a signal
 to close all Gnus servers before the system sleeps.
 
diff --git a/lisp/gnus/gnus-agent.el b/lisp/gnus/gnus-agent.el
index 88873f4..03e447e 100644
--- a/lisp/gnus/gnus-agent.el
+++ b/lisp/gnus/gnus-agent.el
@@ -3934,7 +3934,7 @@ If REREAD is not nil, downloaded articles are marked as 
unread."
                   (mm-with-unibyte-buffer
                     (nnheader-insert-file-contents file)
                     (nnheader-remove-body)
-                    (setq header (nnheader-parse-naked-head)))
+                    (setq header (nnheader-parse-head t)))
                   (setf (mail-header-number header) (car downloaded))
                   (if nov-arts
                       (let ((key (concat "^" (int-to-string (car nov-arts))
diff --git a/lisp/gnus/gnus-cache.el b/lisp/gnus/gnus-cache.el
index 02a8ea7..7ca3bf1 100644
--- a/lisp/gnus/gnus-cache.el
+++ b/lisp/gnus/gnus-cache.el
@@ -186,7 +186,7 @@ it's not cached."
                (gnus-cache-update-file-total-fetched-for group file))
              (setq lines-chars (nnheader-get-lines-and-char))
              (nnheader-remove-body)
-             (setq headers (nnheader-parse-naked-head))
+             (setq headers (nnheader-parse-head t))
              (setf (mail-header-number headers) number)
              (setf (mail-header-lines headers) (car lines-chars))
              (setf (mail-header-chars headers) (cadr lines-chars))
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el
index 673a4d2..e40b2eb 100644
--- a/lisp/gnus/gnus-cloud.el
+++ b/lisp/gnus/gnus-cloud.el
@@ -391,6 +391,8 @@ When FULL is t, upload everything, not just a difference 
from the last full."
             (gnus-group-refresh-group group))
         (gnus-error 2 "Failed to upload Gnus Cloud data to %s" group)))))
 
+(defvar gnus-alter-header-function)
+
 (defun gnus-cloud-add-timestamps (elems)
   (dolist (elem elems)
     (let* ((file-name (plist-get elem :file-name))
@@ -409,9 +411,11 @@ When FULL is t, upload everything, not just a difference 
from the last full."
     (when (gnus-retrieve-headers (gnus-uncompress-range active) group)
       (with-current-buffer nntp-server-buffer
         (goto-char (point-min))
-        (while (and (not (eobp))
-                    (setq head (nnheader-parse-head)))
-          (push head headers))))
+       (while (setq head (nnheader-parse-head))
+          (when gnus-alter-header-function
+            (funcall gnus-alter-header-function head))
+          (push head headers))
+       ))
     (sort (nreverse headers)
           (lambda (h1 h2)
             (> (gnus-cloud-chunk-sequence (mail-header-subject h1))
diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el
index 2cbbe62..ad6e0e3 100644
--- a/lisp/gnus/gnus-group.el
+++ b/lisp/gnus/gnus-group.el
@@ -49,8 +49,6 @@
 (autoload 'gnus-agent-total-fetched-for "gnus-agent")
 (autoload 'gnus-cache-total-fetched-for "gnus-cache")
 
-(autoload 'gnus-group-make-nnir-group "nnir")
-
 (autoload 'gnus-cloud-upload-all-data "gnus-cloud")
 (autoload 'gnus-cloud-download-all-data "gnus-cloud")
 
@@ -663,7 +661,8 @@ simple manner."
   "D" gnus-group-enter-directory
   "f" gnus-group-make-doc-group
   "w" gnus-group-make-web-group
-  "G" gnus-group-make-nnir-group
+  "G" gnus-group-read-ephemeral-search-group
+  "g" gnus-group-make-search-group
   "M" gnus-group-read-ephemeral-group
   "r" gnus-group-rename-group
   "R" gnus-group-make-rss-group
@@ -909,7 +908,8 @@ simple manner."
        ["Add the help group" gnus-group-make-help-group t]
        ["Make a doc group..." gnus-group-make-doc-group t]
        ["Make a web group..." gnus-group-make-web-group t]
-       ["Make a search group..." gnus-group-make-nnir-group t]
+       ["Read a search group..." gnus-group-read-ephemeral-search-group t]
+       ["Make a search group..." gnus-group-make-search-group t]
        ["Make a virtual group..." gnus-group-make-empty-virtual t]
        ["Add a group to a virtual..." gnus-group-add-to-virtual t]
        ["Make an ephemeral group..." gnus-group-read-ephemeral-group t]
@@ -3166,6 +3166,52 @@ mail messages or news articles in files that have 
numeric names."
      (gnus-group-real-name group)
      (list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir)))))
 
+
+(autoload 'nnir-make-specs "nnir")
+(autoload 'gnus-group-topic-name "gnus-topic")
+
+;; Temporary to make group creation easier
+(defun gnus-group-make-search-group (nnir-extra-parms &optional specs)
+  (interactive "P")
+  (let ((name (gnus-read-group "Group name: ")))
+    (with-current-buffer gnus-group-buffer
+      (gnus-group-make-group
+       name
+       (list 'nnselect "nnselect")
+       nil
+       (list
+       (cons 'nnselect-specs
+             (list
+              (cons 'nnselect-function 'nnir-run-query)
+              (cons 'nnselect-args
+                    (nnir-make-specs nnir-extra-parms specs)))))))))
+
+(defun gnus-group-read-ephemeral-search-group (nnir-extra-parms        
&optional specs)
+  "Create an nnselect group based on a search.  Prompt for a
+search query and determine the groups to search as follows: if
+called from the *Server* buffer search all groups belonging to
+the server on the current line; if called from the *Group* buffer
+search any marked groups, or the group on the current line, or
+all the groups under the current topic. Calling with a prefix-arg
+prompts for additional search-engine specific constraints. A
+non-nil `specs' arg must be an alist with `nnir-query-spec' and
+`nnir-group-spec' keys, and skips all prompting."
+  (interactive "P")
+  (gnus-group-read-ephemeral-group
+   (concat "nnselect-" (message-unique-id))
+   (list 'nnselect "nnselect")
+   nil
+   (cons (current-buffer) gnus-current-window-configuration)
+                                       ;     nil
+   nil nil
+   (list
+    (cons 'nnselect-specs
+         (list
+          (cons 'nnselect-function 'nnir-run-query)
+          (cons 'nnselect-args
+                (nnir-make-specs nnir-extra-parms specs))))
+    (cons 'nnselect-artlist nil))))
+
 (defun gnus-group-add-to-virtual (n vgroup)
   "Add the current group to a virtual group."
   (interactive
diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el
index e770abc..7bc7fb5 100644
--- a/lisp/gnus/gnus-msg.el
+++ b/lisp/gnus/gnus-msg.el
@@ -393,10 +393,9 @@ only affect the Gcc copy, but not the original message."
      (gnus-inews-make-draft-meta-information
       ,gnus-newsgroup-name ',articles)))
 
-(autoload 'nnir-article-number "nnir" nil nil 'macro)
-(autoload 'nnir-article-group "nnir" nil nil 'macro)
-(autoload 'gnus-nnir-group-p "nnir")
-
+(autoload 'nnselect-article-number "nnselect" nil nil 'macro)
+(autoload 'nnselect-article-group "nnselect" nil nil 'macro)
+(autoload 'gnus-nnselect-group-p "nnselect")
 
 (defvar gnus-article-reply nil)
 (defmacro gnus-setup-message (config &rest forms)
@@ -404,22 +403,24 @@ only affect the Gcc copy, but not the original message."
        (winconf-name (make-symbol "gnus-setup-message-winconf-name"))
        (buffer (make-symbol "gnus-setup-message-buffer"))
        (article (make-symbol "gnus-setup-message-article"))
+       (oarticle (make-symbol "gnus-setup-message-oarticle"))
        (yanked (make-symbol "gnus-setup-yanked-articles"))
        (group (make-symbol "gnus-setup-message-group")))
     `(let ((,winconf (current-window-configuration))
           (,winconf-name gnus-current-window-configuration)
           (,buffer (buffer-name (current-buffer)))
-          (,article (if (and (gnus-nnir-group-p gnus-newsgroup-name)
-                             gnus-article-reply)
-                        (nnir-article-number (or (car-safe gnus-article-reply)
-                                                 gnus-article-reply))
-                      gnus-article-reply))
+          (,article   (when gnus-article-reply
+                        (or (nnselect-article-number
+                             (or (car-safe gnus-article-reply)
+                                 gnus-article-reply))
+                            gnus-article-reply)))
+          (,oarticle gnus-article-reply)
           (,yanked gnus-article-yanked-articles)
-          (,group (if (and (gnus-nnir-group-p gnus-newsgroup-name)
-                           gnus-article-reply)
-                      (nnir-article-group (or (car-safe gnus-article-reply)
-                                              gnus-article-reply))
-                    gnus-newsgroup-name))
+          (,group (when gnus-article-reply
+                    (or (nnselect-article-group
+                         (or (car-safe gnus-article-reply)
+                             gnus-article-reply))
+                        gnus-newsgroup-name)))
           (message-header-setup-hook
            (copy-sequence message-header-setup-hook))
           (mbl mml-buffer-list)
@@ -460,24 +461,23 @@ only affect the Gcc copy, but not the original message."
        (unwind-protect
           (progn
             ,@forms)
-        (gnus-inews-add-send-actions ,winconf ,buffer ,article ,config
+        (gnus-inews-add-send-actions ,winconf ,buffer ,oarticle ,config
                                      ,yanked ,winconf-name)
         (setq gnus-message-buffer (current-buffer))
         (set (make-local-variable 'gnus-message-group-art)
              (cons ,group ,article))
-        (set (make-local-variable 'gnus-newsgroup-name) ,group)
-        ;; Enable highlighting of different citation levels
-        (when gnus-message-highlight-citation
-          (gnus-message-citation-mode 1))
-        (gnus-run-hooks 'gnus-message-setup-hook)
-        (if (eq major-mode 'message-mode)
-            (let ((mbl1 mml-buffer-list))
-              (setq mml-buffer-list mbl)  ;; Global value
-              (set (make-local-variable 'mml-buffer-list) mbl1);; Local value
-              (add-hook 'change-major-mode-hook 'mml-destroy-buffers nil t)
-              (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t))
-          (mml-destroy-buffers)
-          (setq mml-buffer-list mbl)))
+         ;; Enable highlighting of different citation levels
+         (when gnus-message-highlight-citation
+           (gnus-message-citation-mode 1))
+         (gnus-run-hooks 'gnus-message-setup-hook)
+         (if (eq major-mode 'message-mode)
+             (let ((mbl1 mml-buffer-list))
+               (setq mml-buffer-list mbl)  ;; Global value
+               (set (make-local-variable 'mml-buffer-list) mbl1);; Local value
+               (add-hook 'change-major-mode-hook 'mml-destroy-buffers nil t)
+               (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t))
+           (mml-destroy-buffers)
+           (setq mml-buffer-list mbl)))
        (message-hide-headers)
        (gnus-add-buffer)
        (gnus-configure-windows ,config t)
@@ -521,12 +521,10 @@ instead."
          mail-buf)
       (unwind-protect
          (progn
-           (setq gnus-newsgroup-name "")
+           (let ((gnus-newsgroup-name ""))
            (gnus-setup-message 'message
              (message-mail to subject other-headers continue
-                           nil yank-action send-actions return-action)))
-       (with-current-buffer buf
-         (setq gnus-newsgroup-name group-name)))
+                           nil yank-action send-actions return-action)))))
       (when switch-action
        (setq mail-buf (current-buffer))
        (switch-to-buffer buf)
@@ -617,18 +615,15 @@ If ARG is 1, prompt for a group name to find the posting 
style."
        (buffer (current-buffer)))
     (unwind-protect
        (progn
-         (setq gnus-newsgroup-name
-               (if arg
-                   (if (= 1 (prefix-numeric-value arg))
-                       (gnus-group-completing-read
-                        "Use posting style of group"
-                        nil (gnus-read-active-file-p))
-                     (gnus-group-group-name))
-                 ""))
-         ;; #### see comment in gnus-setup-message -- drv
-         (gnus-setup-message 'message (message-mail)))
-      (with-current-buffer buffer
-       (setq gnus-newsgroup-name group)))))
+         (let ((gnus-newsgroup-name
+                (if arg
+                    (if (= 1 (prefix-numeric-value arg))
+                        (gnus-group-completing-read
+                         "Use posting style of group"
+                         nil (gnus-read-active-file-p))
+                      (gnus-group-group-name))
+                  "")))
+           (gnus-setup-message 'message (message-mail)))))))
 
 (defun gnus-group-news (&optional arg)
   "Start composing a news.
@@ -647,19 +642,16 @@ network.  The corresponding back end must have a 
`request-post' method."
        (buffer (current-buffer)))
     (unwind-protect
        (progn
-         (setq gnus-newsgroup-name
+         (let ((gnus-newsgroup-name
                (if arg
                    (if (= 1 (prefix-numeric-value arg))
                        (gnus-group-completing-read "Use group"
                                                    nil
                                                    (gnus-read-active-file-p))
                      (gnus-group-group-name))
-                 ""))
-         ;; #### see comment in gnus-setup-message -- drv
+                 "")))
          (gnus-setup-message 'message
-           (message-news (gnus-group-real-name gnus-newsgroup-name))))
-      (with-current-buffer buffer
-       (setq gnus-newsgroup-name group)))))
+           (message-news (gnus-group-real-name gnus-newsgroup-name))))))))
 
 (defun gnus-group-post-news (&optional arg)
   "Start composing a message (a news by default).
@@ -694,18 +686,15 @@ posting style."
        (buffer (current-buffer)))
     (unwind-protect
        (progn
-         (setq gnus-newsgroup-name
+         (let ((gnus-newsgroup-name
                (if arg
                    (if (= 1 (prefix-numeric-value arg))
                        (gnus-group-completing-read "Use group"
                                                    nil
                                                    (gnus-read-active-file-p))
                      "")
-                 gnus-newsgroup-name))
-         ;; #### see comment in gnus-setup-message -- drv
-         (gnus-setup-message 'message (message-mail)))
-      (with-current-buffer buffer
-       (setq gnus-newsgroup-name group)))))
+                 gnus-newsgroup-name)))
+         (gnus-setup-message 'message (message-mail)))))))
 
 (defun gnus-summary-news-other-window (&optional arg)
   "Start composing a news in another window.
@@ -724,24 +713,21 @@ network.  The corresponding back end must have a 
`request-post' method."
        (buffer (current-buffer)))
     (unwind-protect
        (progn
-         (setq gnus-newsgroup-name
+         (let ((gnus-newsgroup-name
                (if arg
                    (if (= 1 (prefix-numeric-value arg))
                        (gnus-group-completing-read "Use group"
                                                    nil
                                                    (gnus-read-active-file-p))
                      "")
-                 gnus-newsgroup-name))
-         ;; #### see comment in gnus-setup-message -- drv
+                 gnus-newsgroup-name)))
          (gnus-setup-message 'message
            (progn
              (message-news (gnus-group-real-name gnus-newsgroup-name))
              (set (make-local-variable 'gnus-discouraged-post-methods)
                   (remove
                    (car (gnus-find-method-for-group gnus-newsgroup-name))
-                   gnus-discouraged-post-methods)))))
-      (with-current-buffer buffer
-       (setq gnus-newsgroup-name group)))))
+                   gnus-discouraged-post-methods)))))))))
 
 (defun gnus-summary-post-news (&optional arg)
   "Start composing a message.  Post to the current group by default.
@@ -823,7 +809,7 @@ active, the entire article will be yanked."
             (with-current-buffer gnus-article-copy
               (save-restriction
                 (nnheader-narrow-to-headers)
-                (nnheader-parse-naked-head)))))
+                (nnheader-parse-head t)))))
        (message-yank-original)
        (message-exchange-point-and-mark)
        (setq beg (or beg (mark t))))
@@ -1993,10 +1979,10 @@ process-mark several articles, they will all be 
attached."
     (gnus-summary-iterate n
       (gnus-summary-select-article)
       (with-current-buffer destination
-       ;; Attach at the end of the buffer.
-       (save-excursion
-        (goto-char (point-max))
-        (message-forward-make-body-mime gnus-original-article-buffer))))
+       ;; Attach at the end of the buffer.
+       (save-excursion
+         (goto-char (point-max))
+         (message-forward-make-body-mime gnus-original-article-buffer))))
     (gnus-configure-windows 'message t)))
 
 (provide 'gnus-msg)
diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el
index 1ac1d05..65bcd0e 100644
--- a/lisp/gnus/gnus-registry.el
+++ b/lisp/gnus/gnus-registry.el
@@ -427,6 +427,8 @@ This is not required after changing 
`gnus-registry-cache-file'."
     (gnus-message 4 "Removed %d ignored entries from the Gnus registry"
                   (- old-size (registry-size db)))))
 
+(declare-function gnus-nnselect-group-p "nnselect" (group))
+(declare-function nnselect-article-group "nnselect" (article))
 ;; article move/copy/spool/delete actions
 (defun gnus-registry-action (action data-header from &optional to method)
   (let* ((id (mail-header-id data-header))
@@ -437,7 +439,10 @@ This is not required after changing 
`gnus-registry-cache-file'."
                       (or (cdr-safe (assq 'To extra)) "")))
          (sender (nth 0 (gnus-registry-extract-addresses
                          (mail-header-from data-header))))
-         (from (gnus-group-guess-full-name-from-command-method from))
+         (from (gnus-group-guess-full-name-from-command-method
+                (if (gnus-nnselect-group-p from)
+                    (nnselect-article-group (mail-header-number data-header))
+                  from)))
          (to (if to (gnus-group-guess-full-name-from-command-method to) nil)))
     (gnus-message 7 "Gnus registry: article %s %s from %s to %s"
                   id (if method "respooling" "going") from to)
@@ -788,7 +793,7 @@ Consults `gnus-registry-unfollowed-groups' and
 Consults `gnus-registry-ignored-groups' and
 `nnmail-split-fancy-with-parent-ignore-groups'."
   (and group
-       (or (gnus-grep-in-list
+       (or (gnus-virtual-group-p group) (gnus-grep-in-list
             group
             (delq nil (mapcar (lambda (g)
                                 (cond
@@ -1218,7 +1223,7 @@ is `ask', ask the user; or if `gnus-registry-install' is 
non-nil, enable it."
       (gnus-registry-initialize)))
   gnus-registry-enabled)
 
-;; largely based on nnir-warp-to-article
+;; largely based on nnselect-warp-to-article
 (defun gnus-try-warping-via-registry ()
   "Try to warp via the registry.
 This will be done via the current article's source group based on
@@ -1242,7 +1247,7 @@ data stored in the registry."
                       (gnus-ephemeral-group-p group) ;; any ephemeral group
                       (memq (car (gnus-find-method-for-group group))
                            ;; Specific methods; this list may need to expand.
-                            '(nnir)))
+                            '(nnselect)))
 
             ;; remember that we've seen this group already
             (push group seen-groups)
diff --git a/lisp/gnus/gnus-srvr.el b/lisp/gnus/gnus-srvr.el
index 095e054..8cb80b2 100644
--- a/lisp/gnus/gnus-srvr.el
+++ b/lisp/gnus/gnus-srvr.el
@@ -34,7 +34,8 @@
 (require 'gnus-range)
 (require 'gnus-cloud)
 
-(autoload 'gnus-group-make-nnir-group "nnir")
+(autoload 'gnus-group-read-ephemeral-search-group "nnselect")
+;;(autoload 'gnus-group-make-permanent-search-group "nnselect")
 
 (defcustom gnus-server-exit-hook nil
   "Hook run when exiting the server buffer."
@@ -176,7 +177,7 @@ If nil, a faster, but more primitive, buffer is used 
instead."
 
     "g" gnus-server-regenerate-server
 
-    "G" gnus-group-make-nnir-group
+    "G" gnus-group-read-ephemeral-search-group
 
     "z" gnus-server-compact-server
 
diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el
index fe600f1..e4f05de 100644
--- a/lisp/gnus/gnus-start.el
+++ b/lisp/gnus/gnus-start.el
@@ -1802,7 +1802,7 @@ backend check whether the group actually exists."
      ;; by one.
      (t
       (dolist (info infos)
-       (gnus-activate-group (gnus-info-group info) nil nil method t))))))
+       (gnus-activate-group (gnus-info-group info) t nil method t))))))
 
 (defun gnus-make-hashtable-from-newsrc-alist ()
   "Create a hash table from `gnus-newsrc-alist'.
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index c53f81f..8f37fc8 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -85,8 +85,8 @@
 (autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t)
 (autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t)
 (autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t)
-(autoload 'nnir-article-rsv "nnir" nil nil 'macro)
-(autoload 'nnir-article-group "nnir" nil nil 'macro)
+(autoload 'nnselect-article-rsv "nnselect" nil nil)
+(autoload 'nnselect-article-group "nnselect" nil nil)
 
 (defcustom gnus-kill-summary-on-exit t
   "If non-nil, kill the summary buffer when you exit from it.
@@ -144,9 +144,9 @@ If t, fetch all the available old headers."
   :type '(choice number
                 (sexp :menu-tag "other" t)))
 
-(defcustom gnus-refer-thread-use-nnir nil
-  "Use nnir to search an entire server when referring threads.
-A nil value will only search for thread-related articles in the
+(defcustom gnus-refer-thread-use-search nil
+  "Search an entire server when referring threads. A
+nil value will only search for thread-related articles in the
 current group."
   :version "24.1"
   :group 'gnus-thread
@@ -884,6 +884,7 @@ controls how articles are sorted."
                           (function-item gnus-article-sort-by-subject)
                           (function-item gnus-article-sort-by-date)
                           (function-item gnus-article-sort-by-score)
+                          (function-item gnus-article-sort-by-rsv)
                           (function-item gnus-article-sort-by-random)
                           (function :tag "other"))
                   (boolean :tag "Reverse order"))))
@@ -927,6 +928,7 @@ subthreads, customize `gnus-subthread-sort-functions'."
                    (function-item gnus-thread-sort-by-subject)
                    (function-item gnus-thread-sort-by-date)
                    (function-item gnus-thread-sort-by-score)
+                   (function-item gnus-thread-sort-by-rsv)
                    (function-item gnus-thread-sort-by-most-recent-number)
                    (function-item gnus-thread-sort-by-most-recent-date)
                    (function-item gnus-thread-sort-by-random)
@@ -1433,16 +1435,13 @@ the normal Gnus MIME machinery."
     (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
     (?k (gnus-summary-line-message-size gnus-tmp-header) ?s)
     (?L gnus-tmp-lines ?s)
-    (?Z (or (nnir-article-rsv (mail-header-number gnus-tmp-header))
-           0)
-        ?d)
-    (?G (or (nnir-article-group (mail-header-number gnus-tmp-header))
-           "")
-        ?s)
+    (?Z (or (nnselect-article-rsv (mail-header-number gnus-tmp-header))
+           0) ?d)
+    (?G (or (nnselect-article-group (mail-header-number gnus-tmp-header))
+           "") ?s)
     (?g (or (gnus-group-short-name
-            (nnir-article-group (mail-header-number gnus-tmp-header)))
-           "")
-        ?s)
+            (nnselect-article-group (mail-header-number gnus-tmp-header)))
+           "") ?s)
     (?O gnus-tmp-downloaded ?c)
     (?I gnus-tmp-indentation ?s)
     (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s)
@@ -1619,6 +1618,8 @@ This list will always be a subset of 
gnus-newsgroup-undownloaded.")
 
 (defvar gnus-newsgroup-sparse nil)
 
+(defvar gnus-newsgroup-selection nil)
+
 (defvar gnus-current-article nil)
 (defvar gnus-article-current nil)
 (defvar gnus-current-headers nil)
@@ -1653,6 +1654,8 @@ This list will always be a subset of 
gnus-newsgroup-undownloaded.")
     gnus-newsgroup-undownloaded
     gnus-newsgroup-unsendable
 
+    gnus-newsgroup-selection
+
     gnus-newsgroup-begin gnus-newsgroup-end
     gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
     gnus-newsgroup-last-folder gnus-newsgroup-last-file
@@ -4532,48 +4535,14 @@ Returns HEADER if it was entered in the DEPENDENCIES.  
Returns nil otherwise."
 ;; This function has to be called with point after the article number
 ;; on the beginning of the line.
 (defsubst gnus-nov-parse-line (number dependencies &optional force-new)
-  (let ((eol (point-at-eol))
-       header references in-reply-to)
-
+  (let (header)
     ;; overview: [num subject from date id refs chars lines misc]
     (unwind-protect
-       (let (x)
-         (narrow-to-region (point) eol)
-         (unless (eobp)
-           (forward-char))
-
-         (setq header
-               (make-full-mail-header
-                number                 ; number
-                (condition-case ()     ; subject
-                    (gnus-remove-odd-characters
-                     (funcall gnus-decode-encoded-word-function
-                              (setq x (nnheader-nov-field))))
-                  (error x))
-                (condition-case ()     ; from
-                    (gnus-remove-odd-characters
-                     (funcall gnus-decode-encoded-address-function
-                              (setq x (nnheader-nov-field))))
-                  (error x))
-                (nnheader-nov-field)   ; date
-                (nnheader-nov-read-message-id number)  ; id
-                (setq references (nnheader-nov-field)) ; refs
-                (nnheader-nov-read-integer) ; chars
-                (nnheader-nov-read-integer) ; lines
-                (unless (eobp)
-                  (if (looking-at "Xref: ")
-                      (goto-char (match-end 0)))
-                  (nnheader-nov-field)) ; Xref
-                (nnheader-nov-parse-extra)))) ; extra
-
+       (narrow-to-region (point) (point-at-eol))
+      (unless (eobp)
+       (forward-char))
+      (setq header (nnheader-parse-nov number))
       (widen))
-
-    (when (and (string= references "")
-              (setq in-reply-to (mail-header-extra header))
-              (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
-      (setf (mail-header-references header)
-            (gnus-extract-message-id-from-in-reply-to in-reply-to)))
-
     (when gnus-alter-header-function
       (funcall gnus-alter-header-function header))
     (gnus-dependencies-add-header header dependencies force-new)))
@@ -5104,6 +5073,17 @@ using some other form will lead to serious barfage."
   (gnus-article-sort-by-date
    (gnus-thread-header h1) (gnus-thread-header h2)))
 
+(defsubst gnus-article-sort-by-rsv (h1 h2)
+  "Sort articles by rsv."
+  (when gnus-newsgroup-selection
+    (< (nnselect-article-rsv (mail-header-number h1))
+       (nnselect-article-rsv (mail-header-number h2)))))
+
+(defun gnus-thread-sort-by-rsv (h1 h2)
+  "Sort threads by root article rsv."
+  (gnus-article-sort-by-rsv
+   (gnus-thread-header h1) (gnus-thread-header h2)))
+
 (defsubst gnus-article-sort-by-score (h1 h2)
   "Sort articles by root article score.
 Unscored articles will be counted as having a score of zero."
@@ -5634,22 +5614,32 @@ or a straight list of headers."
   "Fetch headers of ARTICLES."
   (gnus-message 7 "Fetching headers for %s..." gnus-newsgroup-name)
   (prog1
-      (if (eq 'nov
-             (setq gnus-headers-retrieved-by
-                   (gnus-retrieve-headers
-                    articles gnus-newsgroup-name
-                    (or limit
-                        ;; We might want to fetch old headers, but
-                        ;; not if there is only 1 article.
-                        (and (or (and
-                                  (not (eq gnus-fetch-old-headers 'some))
-                                  (not (numberp gnus-fetch-old-headers)))
-                                 (> (length articles) 1))
-                             gnus-fetch-old-headers)))))
-         (gnus-get-newsgroup-headers-xover
-          articles force-new dependencies gnus-newsgroup-name t)
-       (gnus-get-newsgroup-headers dependencies force-new))
-    (gnus-message 7 "Fetching headers for %s...done" gnus-newsgroup-name)))
+      (pcase (setq gnus-headers-retrieved-by
+                  (gnus-retrieve-headers
+                   articles gnus-newsgroup-name
+                   (or limit
+                       ;; We might want to fetch old headers, but
+                       ;; not if there is only 1 article.
+                       (and (or (and
+                                 (not (eq gnus-fetch-old-headers 'some))
+                                 (not (numberp gnus-fetch-old-headers)))
+                                (> (length articles) 1))
+                            gnus-fetch-old-headers))))
+    ('nov
+     (gnus-get-newsgroup-headers-xover
+      articles force-new dependencies gnus-newsgroup-name t))
+    ('headers
+     (gnus-get-newsgroup-headers dependencies force-new))
+    ((pred listp)
+     (let ((dependencies
+           (or dependencies
+               (with-current-buffer gnus-summary-buffer
+                 gnus-newsgroup-dependencies))))
+     (delq nil (mapcar   #'(lambda (header)
+                            (gnus-dependencies-add-header
+                             header dependencies force-new))
+                        gnus-headers-retrieved-by)))))
+  (gnus-message 7 "Fetching headers for %s...done" gnus-newsgroup-name)))
 
 (defun gnus-select-newsgroup (group &optional read-all select-articles)
   "Select newsgroup GROUP.
@@ -6405,12 +6395,11 @@ The resulting hash table is returned, or nil if no 
Xrefs were found."
          (gnus-group-update-group group t))))))
 
 (defun gnus-get-newsgroup-headers (&optional dependencies force-new)
-  (let ((cur nntp-server-buffer)
-       (dependencies
+  (let ((dependencies
         (or dependencies
             (with-current-buffer gnus-summary-buffer
               gnus-newsgroup-dependencies)))
-       headers id end ref number
+       headers
        (mail-parse-charset gnus-newsgroup-charset)
        (mail-parse-ignored-charsets
         (save-current-buffer (condition-case nil
@@ -6418,146 +6407,15 @@ The resulting hash table is returned, or nil if no 
Xrefs were found."
                                 (error))
                               gnus-newsgroup-ignored-charsets)))
     (with-current-buffer nntp-server-buffer
-      ;; Translate all TAB characters into SPACE characters.
-      (subst-char-in-region (point-min) (point-max) ?\t ?  t)
-      (subst-char-in-region (point-min) (point-max) ?\r ?  t)
-      (ietf-drums-unfold-fws)
       (gnus-run-hooks 'gnus-parse-headers-hook)
-      (let ((case-fold-search t)
-           in-reply-to header p lines chars)
+      (let ((nnmail-extra-headers gnus-extra-headers)
+           header)
        (goto-char (point-min))
-       ;; Search to the beginning of the next header.  Error messages
-       ;; do not begin with 2 or 3.
-       (while (re-search-forward "^[23][0-9]+ " nil t)
-         (setq id nil
-               ref nil)
-         ;; This implementation of this function, with nine
-         ;; search-forwards instead of the one re-search-forward and
-         ;; a case (which basically was the old function) is actually
-         ;; about twice as fast, even though it looks messier.  You
-         ;; can't have everything, I guess.  Speed and elegance
-         ;; doesn't always go hand in hand.
-         (setq
-          header
-          (make-full-mail-header
-           ;; Number.
-           (prog1
-               (setq number (read cur))
-             (end-of-line)
-             (setq p (point))
-             (narrow-to-region (point)
-                               (or (and (search-forward "\n.\n" nil t)
-                                        (- (point) 2))
-                                   (point))))
-           ;; Subject.
-           (progn
-             (goto-char p)
-             (if (search-forward "\nsubject:" nil t)
-                 (funcall gnus-decode-encoded-word-function
-                          (nnheader-header-value))
-               "(none)"))
-           ;; From.
-           (progn
-             (goto-char p)
-             (if (search-forward "\nfrom:" nil t)
-                 (funcall gnus-decode-encoded-address-function
-                          (nnheader-header-value))
-               "(nobody)"))
-           ;; Date.
-           (progn
-             (goto-char p)
-             (if (search-forward "\ndate:" nil t)
-                 (nnheader-header-value) ""))
-           ;; Message-ID.
-           (progn
-             (goto-char p)
-             (setq id (if (re-search-forward
-                           "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
-                          ;; We do it this way to make sure the Message-ID
-                          ;; is (somewhat) syntactically valid.
-                          (buffer-substring (match-beginning 1)
-                                            (match-end 1))
-                        ;; If there was no message-id, we just fake one
-                        ;; to make subsequent routines simpler.
-                        (nnheader-generate-fake-message-id number))))
-           ;; References.
-           (progn
-             (goto-char p)
-             (if (search-forward "\nreferences:" nil t)
-                 (progn
-                   (setq end (point))
-                   (prog1
-                       (nnheader-header-value)
-                     (setq ref
-                           (buffer-substring
-                            (progn
-                              (end-of-line)
-                              (search-backward ">" end t)
-                              (1+ (point)))
-                            (progn
-                              (search-backward "<" end t)
-                              (point))))))
-               ;; Get the references from the in-reply-to header if there
-               ;; were no references and the in-reply-to header looks
-               ;; promising.
-               (if (and (search-forward "\nin-reply-to:" nil t)
-                        (setq in-reply-to (nnheader-header-value))
-                        (string-match "<[^>]+>" in-reply-to))
-                   (let (ref2)
-                     (setq ref (substring in-reply-to (match-beginning 0)
-                                          (match-end 0)))
-                     (while (string-match "<[^>]+>" in-reply-to (match-end 0))
-                       (setq ref2 (substring in-reply-to (match-beginning 0)
-                                             (match-end 0)))
-                       (when (> (length ref2) (length ref))
-                         (setq ref ref2)))
-                     ref)
-                 (setq ref nil))))
-           ;; Chars.
-           (progn
-             (goto-char p)
-             (if (search-forward "\nchars: " nil t)
-                 (if (numberp (setq chars (ignore-errors (read cur))))
-                     chars -1)
-               -1))
-           ;; Lines.
-           (progn
-             (goto-char p)
-             (if (search-forward "\nlines: " nil t)
-                 (if (numberp (setq lines (ignore-errors (read cur))))
-                     lines -1)
-               -1))
-           ;; Xref.
-           (progn
-             (goto-char p)
-             (and (search-forward "\nxref:" nil t)
-                  (nnheader-header-value)))
-           ;; Extra.
-           (when gnus-extra-headers
-             (let ((extra gnus-extra-headers)
-                   out)
-               (while extra
-                 (goto-char p)
-                 (when (search-forward
-                        (concat "\n" (symbol-name (car extra)) ":") nil t)
-                   (push (cons (car extra) (nnheader-header-value))
-                         out))
-                 (pop extra))
-               out))))
-         (when (equal id ref)
-           (setq ref nil))
-
-         (when gnus-alter-header-function
-           (funcall gnus-alter-header-function header)
-           (setq id (mail-header-id header)
-                 ref (gnus-parent-id (mail-header-references header))))
-
+       (while (setq header (nnheader-parse-head))
          (when (setq header
                      (gnus-dependencies-add-header
                       header dependencies force-new))
-           (push header headers))
-         (goto-char (point-max))
-         (widen))
+           (push header headers)))
        (nreverse headers)))))
 
 ;; Goes through the xover lines and returns a list of vectors
@@ -8702,7 +8560,8 @@ SCORE."
 When called interactively, ID is the Message-ID of the current
 article.  If thread-only is non-nil limit the summary buffer to
 these articles."
-  (interactive (list (mail-header-id (gnus-summary-article-header))))
+  (interactive (list (mail-header-id (gnus-summary-article-header))
+                    current-prefix-arg))
   (let ((articles (gnus-articles-in-thread
                   (gnus-id-to-thread (gnus-root-id id))))
        ;;we REALLY want the whole thread---this prevents cut-threads
@@ -9125,13 +8984,13 @@ Return the number of articles fetched."
     result))
 
 (defun gnus-summary-refer-thread (&optional limit)
-  "Fetch all articles in the current thread.  For backends
-that know how to search for threads (currently only 'nnimap)
-a non-numeric prefix arg will use nnir to search the entire
+  "Fetch all articles in the current thread. For backends that
+know how to search for threads (currently only 'nnimap) a
+non-numeric prefix arg will search the entire
 server; without a prefix arg only the current group is
-searched.  If the variable `gnus-refer-thread-use-nnir' is
-non-nil the prefix arg has the reverse meaning.  If no
-backend-specific `request-thread' function is available fetch
+searched. If the variable `gnus-refer-thread-use-search' is
+non-nil the prefix arg has the reverse meaning. If no
+backend-specific 'request-thread function is available fetch
 LIMIT (the numerical prefix) old headers.  If LIMIT is
 non-numeric or nil fetch the number specified by the
 `gnus-refer-thread-limit' variable."
@@ -9141,9 +9000,9 @@ non-numeric or nil fetch the number specified by the
         (gnus-inhibit-demon t)
         (gnus-summary-ignore-duplicates t)
         (gnus-read-all-available-headers t)
-        (gnus-refer-thread-use-nnir
+        (gnus-refer-thread-use-search
          (if (and (not (null limit)) (listp limit))
-             (not gnus-refer-thread-use-nnir) gnus-refer-thread-use-nnir))
+             (not gnus-refer-thread-use-search) gnus-refer-thread-use-search))
         (new-headers
          (if (gnus-check-backend-function
               'request-thread gnus-newsgroup-name)
@@ -9284,9 +9143,9 @@ non-numeric or nil fetch the number specified by the
       (dolist (method gnus-refer-article-method)
        (push (if (eq 'current method)
                  gnus-current-select-method
-               (if (eq 'nnir (car method))
+               (if (eq 'nnselect (car method))
                    (list
-                    'nnir
+                    'nnselect
                     (or (cadr method)
                         (gnus-method-to-server gnus-current-select-method)))
                  method))
diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el
index 295395c..4e3fc98 100644
--- a/lisp/gnus/gnus.el
+++ b/lisp/gnus/gnus.el
@@ -1607,7 +1607,7 @@ total number of articles in the group.")
  :variable-default (mapcar
                     (lambda (g) (list g t))
                     '("delayed$" "drafts$" "queue$" "INBOX$"
-                      "^nnmairix:" "^nnir:" "archive"))
+                      "^nnmairix:" "^nnselect:" "archive"))
  :variable-document
  "Groups in which the registry should be turned off."
  :variable-group gnus-registry
@@ -3153,7 +3153,10 @@ that that variable is buffer-local to the summary 
buffers."
 
 (defun gnus-kill-ephemeral-group (group)
   "Remove ephemeral GROUP from relevant structures."
-  (remhash group gnus-newsrc-hashtb))
+  (remhash group gnus-newsrc-hashtb)
+    (setq gnus-newsrc-alist
+       (delq (assoc group gnus-newsrc-alist)
+              gnus-newsrc-alist)))
 
 (defun gnus-simplify-mode-line ()
   "Make mode lines a bit simpler."
diff --git a/lisp/gnus/nndiary.el b/lisp/gnus/nndiary.el
index 945ef03..7894285 100644
--- a/lisp/gnus/nndiary.el
+++ b/lisp/gnus/nndiary.el
@@ -992,7 +992,7 @@ all.  This may very well take some time.")
        (narrow-to-region
         (goto-char (point-min))
         (if (search-forward "\n\n" nil t) (1- (point)) (point-max))))
-      (let ((headers (nnheader-parse-naked-head)))
+      (let ((headers (nnheader-parse-head t)))
        (setf (mail-header-chars  headers) chars)
        (setf (mail-header-number headers) number)
        headers))))
diff --git a/lisp/gnus/nnfolder.el b/lisp/gnus/nnfolder.el
index c27af17..6ff9905 100644
--- a/lisp/gnus/nnfolder.el
+++ b/lisp/gnus/nnfolder.el
@@ -1160,7 +1160,7 @@ This command does not work if you use short group names."
        (if (search-forward "\n\n" e t) (setq e (1- (point)))))
       (with-temp-buffer
        (insert-buffer-substring buf b e)
-       (let ((headers (nnheader-parse-naked-head)))
+       (let ((headers (nnheader-parse-head t)))
          (setf (mail-header-chars  headers) chars)
          (setf (mail-header-number headers) number)
          headers)))))
diff --git a/lisp/gnus/nnheader.el b/lisp/gnus/nnheader.el
index fee7a16..1a50697 100644
--- a/lisp/gnus/nnheader.el
+++ b/lisp/gnus/nnheader.el
@@ -28,6 +28,10 @@
 
 (eval-when-compile (require 'cl-lib))
 
+(defvar gnus-decode-encoded-word-function)
+(defvar gnus-decode-encoded-address-function)
+(defvar gnus-alter-header-function)
+
 (defvar nnmail-extra-headers)
 (defvar gnus-newsgroup-name)
 (defvar jka-compr-compression-info-list)
@@ -39,6 +43,7 @@
 (require 'mail-utils)
 (require 'mm-util)
 (require 'gnus-util)
+(autoload 'gnus-remove-odd-characters "gnus-sum")
 (autoload 'gnus-range-add "gnus-range")
 (autoload 'gnus-remove-from-range "gnus-range")
 ;; FIXME none of these are used explicitly in this file.
@@ -188,124 +193,167 @@ on your system, you could say something like:
 
 (autoload 'ietf-drums-unfold-fws "ietf-drums")
 
-(defun nnheader-parse-naked-head (&optional number)
-  ;; This function unfolds continuation lines in this buffer
-  ;; destructively.  When this side effect is unwanted, use
-  ;; `nnheader-parse-head' instead of this function.
-  (let ((case-fold-search t)
-       (buffer-read-only nil)
+
+(defsubst nnheader-head-make-header (number)
+  "Using data of type 'head in the current buffer
+  return a full mail header with article NUMBER."
+  (let ((p (point-min))
        (cur (current-buffer))
-       (p (point-min))
-       in-reply-to lines ref)
-    (nnheader-remove-cr-followed-by-lf)
-    (ietf-drums-unfold-fws)
-    (subst-char-in-region (point-min) (point-max) ?\t ? )
-    (goto-char p)
-    (insert "\n")
-    (prog1
-       ;; This implementation of this function, with nine
-       ;; search-forwards instead of the one re-search-forward and a
-       ;; case (which basically was the old function) is actually
-       ;; about twice as fast, even though it looks messier.  You
-       ;; can't have everything, I guess.  Speed and elegance don't
-       ;; always go hand in hand.
-       (make-full-mail-header
-        ;; Number.
-        (or number 0)
-        ;; Subject.
-        (progn
-          (goto-char p)
-          (if (search-forward "\nsubject:" nil t)
-              (nnheader-header-value) "(none)"))
-        ;; From.
-        (progn
-          (goto-char p)
-          (if (search-forward "\nfrom:" nil t)
-              (nnheader-header-value) "(nobody)"))
-        ;; Date.
-        (progn
-          (goto-char p)
-          (if (search-forward "\ndate:" nil t)
-              (nnheader-header-value) ""))
-        ;; Message-ID.
-        (progn
-          (goto-char p)
-          (if (search-forward "\nmessage-id:" nil t)
-              (buffer-substring
-               (1- (or (search-forward "<" (point-at-eol) t)
-                       (point)))
-               (or (search-forward ">" (point-at-eol) t) (point)))
-            ;; If there was no message-id, we just fake one to make
-            ;; subsequent routines simpler.
-            (nnheader-generate-fake-message-id number)))
-        ;; References.
-        (progn
+       in-reply-to chars lines end ref)
+    ;; This implementation of this function, with nine
+    ;; search-forwards instead of the one re-search-forward and a
+    ;; case (which basically was the old function) is actually
+    ;; about twice as fast, even though it looks messier.  You
+    ;; can't have everything, I guess.  Speed and elegance don't
+    ;; always go hand in hand.
+    (make-full-mail-header
+     ;; Number.
+     number
+     ;; Subject.
+     (progn
+       (goto-char p)
+       (if (search-forward "\nsubject:" nil t)
+          (funcall gnus-decode-encoded-word-function
+                   (nnheader-header-value))
+        "(none)"))
+     ;; From.
+     (progn
+       (goto-char p)
+       (if (search-forward "\nfrom:" nil t)
+          (funcall gnus-decode-encoded-address-function
+                   (nnheader-header-value))
+        "(nobody)"))
+     ;; Date.
+     (progn
+       (goto-char p)
+       (if (search-forward "\ndate:" nil t)
+          (nnheader-header-value) ""))
+     ;; Message-ID.
+     (progn
+       (goto-char p)
+       (if (re-search-forward
+           "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
+          ;; We do it this way to make sure the Message-ID
+          ;; is (somewhat) syntactically valid.
+          (buffer-substring (match-beginning 1)
+                            (match-end 1))
+        ;; If there was no message-id, we just fake one to make
+        ;; subsequent routines simpler.
+        (nnheader-generate-fake-message-id number)))
+     ;; References.
+     (progn
+       (goto-char p)
+       (if (search-forward "\nreferences:" nil t)
+          (progn
+            (setq end (point))
+            (prog1
+                (nnheader-header-value)
+              (setq ref
+                    (buffer-substring
+                     (progn
+                       (end-of-line)
+                       (search-backward ">" end t)
+                       (1+ (point)))
+                     (progn
+                       (search-backward "<" end t)
+                       (point))))))
+        ;; Get the references from the in-reply-to header if there
+        ;; were no references and the in-reply-to header looks
+        ;; promising.
+        (if (and (search-forward "\nin-reply-to:" nil t)
+                 (setq in-reply-to (nnheader-header-value))
+                 (string-match "<[^>]+>" in-reply-to))
+            (let (ref2)
+              (setq ref (substring in-reply-to (match-beginning 0)
+                                   (match-end 0)))
+              (while (string-match "<[^>]+>" in-reply-to (match-end 0))
+                (setq ref2 (substring in-reply-to (match-beginning 0)
+                                      (match-end 0)))
+                (when (> (length ref2) (length ref))
+                  (setq ref ref2)))
+              ref)
+          nil)))
+     ;; Chars.
+     (progn
+       (goto-char p)
+       (if (search-forward "\nchars: " nil t)
+          (if (numberp (setq chars (ignore-errors (read cur))))
+              chars -1)
+        -1))
+     ;; Lines.
+     (progn
+       (goto-char p)
+       (if (search-forward "\nlines: " nil t)
+          (if (numberp (setq lines (ignore-errors (read cur))))
+              lines -1)
+        -1))
+     ;; Xref.
+     (progn
+       (goto-char p)
+       (and (search-forward "\nxref:" nil t)
+           (nnheader-header-value)))
+     ;; Extra.
+     (when nnmail-extra-headers
+       (let ((extra nnmail-extra-headers)
+            out)
+        (while extra
           (goto-char p)
-          (if (search-forward "\nreferences:" nil t)
-              (nnheader-header-value)
-            ;; Get the references from the in-reply-to header if
-            ;; there were no references and the in-reply-to header
-            ;; looks promising.
-            (if (and (search-forward "\nin-reply-to:" nil t)
-                     (setq in-reply-to (nnheader-header-value))
-                     (string-match "<[^\n>]+>" in-reply-to))
-                (let (ref2)
-                  (setq ref (substring in-reply-to (match-beginning 0)
-                                       (match-end 0)))
-                  (while (string-match "<[^\n>]+>"
-                                       in-reply-to (match-end 0))
-                    (setq ref2 (substring in-reply-to (match-beginning 0)
-                                          (match-end 0)))
-                    (when (> (length ref2) (length ref))
-                      (setq ref ref2)))
-                  ref)
-              nil)))
-        ;; Chars.
-        0
-        ;; Lines.
-        (progn
-          (goto-char p)
-          (if (search-forward "\nlines: " nil t)
-              (if (numberp (setq lines (read cur)))
-                  lines 0)
-            0))
-        ;; Xref.
-        (progn
-          (goto-char p)
-          (and (search-forward "\nxref:" nil t)
-               (nnheader-header-value)))
-        ;; Extra.
-        (when nnmail-extra-headers
-          (let ((extra nnmail-extra-headers)
-                out)
-            (while extra
-              (goto-char p)
-              (when (search-forward
-                     (concat "\n" (symbol-name (car extra)) ":") nil t)
-                (push (cons (car extra) (nnheader-header-value))
-                      out))
-              (pop extra))
-            out)))
-      (goto-char p)
-      (delete-char 1))))
-
-(defun nnheader-parse-head (&optional naked)
-  (let ((cur (current-buffer)) num beg end)
-    (when (if naked
-             (setq num 0
-                   beg (point-min)
-                   end (point-max))
-           ;; Search to the beginning of the next header.  Error
-           ;; messages do not begin with 2 or 3.
-           (when (re-search-forward "^[23][0-9]+ " nil t)
-             (setq num (read cur)
-                   beg (point)
-                   end (if (search-forward "\n.\n" nil t)
-                           (goto-char  (- (point) 2))
-                         (point)))))
-      (with-temp-buffer
-       (insert-buffer-substring cur beg end)
-       (nnheader-parse-naked-head num)))))
+          (when (search-forward
+                 (concat "\n" (symbol-name (car extra)) ":") nil t)
+            (push (cons (car extra) (nnheader-header-value))
+                  out))
+          (pop extra))
+        out)))))
+
+(defun nnheader-parse-head (&optional naked temp)
+  "Parse data of type 'header in the current buffer and return a
+  mail header, modifying the buffer contents in the process. The
+  buffer is assumed to begin each header with an \"Article
+  retrieved\" line with an article number; If NAKED is non-nil
+  this line is assumed absent, and the buffer should contain a
+  single header's worth of data. If TEMP is non-nil the data is
+  first copied to a temporary buffer leaving the original buffer
+  untouched."
+  (let ((cur (current-buffer))
+       (num 0)
+       (beg (point-min))
+       (end (point-max))
+       buf)
+    (when (or naked
+             ;; Search to the beginning of the next header.  Error
+             ;; messages do not begin with 2 or 3.
+             (when (re-search-forward "^[23][0-9]+ " nil t)
+               (setq num (read cur)
+                     beg (point)
+                     end (if (search-forward "\n.\n" nil t)
+                             (goto-char  (- (point) 2))
+                           (point)))))
+      ;; When TEMP copy the data to a temporary buffer
+      (if temp
+         (progn
+           (set-buffer (setq buf (generate-new-buffer " *nnheader-temp*")))
+           (insert-buffer-substring cur beg end))
+       ;; Otherwise just narrow to the data
+       (narrow-to-region beg end))
+      (let ((case-fold-search t)
+           (buffer-read-only nil)
+           header)
+       (nnheader-remove-cr-followed-by-lf)
+       (ietf-drums-unfold-fws)
+       (subst-char-in-region (point-min) (point-max) ?\t ?  t)
+       (subst-char-in-region (point-min) (point-max) ?\r ?  t)
+       (goto-char (point-min))
+       (insert "\n")
+       (setq header (nnheader-head-make-header num))
+       (goto-char (point-min))
+       (delete-char 1)
+       (if temp
+           (kill-buffer buf)
+         (goto-char (point-max))
+         (widen))
+       (when gnus-alter-header-function
+         (funcall gnus-alter-header-function header))
+       header))))
 
 (defmacro nnheader-nov-skip-field ()
   '(search-forward "\t" eol 'move))
@@ -347,24 +395,43 @@ on your system, you could say something like:
            'id)
        (nnheader-generate-fake-message-id ,number))))
 
-(defun nnheader-parse-nov ()
+(defalias 'nnheader-nov-make-header 'nnheader-parse-nov)
+(autoload 'gnus-extract-message-id-from-in-reply-to "gnus-sum")
+
+(defun nnheader-parse-nov (&optional number)
   (let ((eol (point-at-eol))
-       (number (nnheader-nov-read-integer)))
-    (vector
-     number                            ; number
-     (nnheader-nov-field)              ; subject
-     (nnheader-nov-field)              ; from
-     (nnheader-nov-field)              ; date
-     (nnheader-nov-read-message-id number) ; id
-     (nnheader-nov-field)              ; refs
-     (nnheader-nov-read-integer)       ; chars
-     (nnheader-nov-read-integer)       ; lines
-     (if (eq (char-after) ?\n)
-        nil
-       (if (looking-at "Xref: ")
-          (goto-char (match-end 0)))
-       (nnheader-nov-field))           ; Xref
-     (nnheader-nov-parse-extra))))     ; extra
+       references in-reply-to x header)
+      (setq header
+           (make-full-mail-header
+            (or number (nnheader-nov-read-integer))      ; number
+            (condition-case () ; subject
+                (gnus-remove-odd-characters
+                 (funcall gnus-decode-encoded-word-function
+                          (setq x (nnheader-nov-field))))
+              (error x))
+            (condition-case () ; from
+                (gnus-remove-odd-characters
+                 (funcall gnus-decode-encoded-address-function
+                          (setq x (nnheader-nov-field))))
+              (error x))
+            (nnheader-nov-field)       ; date
+            (nnheader-nov-read-message-id number)      ; id
+            (setq references (nnheader-nov-field))     ; refs
+            (nnheader-nov-read-integer) ; chars
+            (nnheader-nov-read-integer) ; lines
+            (unless (eobp)
+              (if (looking-at "Xref: ")
+                  (goto-char (match-end 0)))
+              (nnheader-nov-field)) ; Xref
+            (nnheader-nov-parse-extra))) ; extra
+
+      (when (and (string= references "")
+                (setq in-reply-to (mail-header-extra header))
+                (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
+       (setf (mail-header-references header)
+              (gnus-extract-message-id-from-in-reply-to in-reply-to)))
+      header))
+
 
 (defun nnheader-insert-nov (header)
   (princ (mail-header-number header) (current-buffer))
@@ -399,17 +466,6 @@ on your system, you could say something like:
       (delete-char 1))
     (forward-line 1)))
 
-(defun nnheader-parse-overview-file (file)
-  "Parse FILE and return a list of headers."
-  (mm-with-unibyte-buffer
-    (nnheader-insert-file-contents file)
-    (goto-char (point-min))
-    (let (headers)
-      (while (not (eobp))
-       (push (nnheader-parse-nov) headers)
-       (forward-line 1))
-      (nreverse headers))))
-
 (defun nnheader-write-overview-file (file headers)
   "Write HEADERS to FILE."
   (with-temp-file file
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index 507e12a..d797e89 100644
--- a/lisp/gnus/nnimap.el
+++ b/lisp/gnus/nnimap.el
@@ -1686,7 +1686,7 @@ If LIMIT, first try to limit the search to the N last 
articles."
         (gnus-add-to-range
          (gnus-add-to-range
           (gnus-range-add (gnus-info-read info)
-                         vanished)
+                         vanished)
          (cdr (assq '%Flagged flags)))
         (cdr (assq '%Seen flags))))
   (let ((marks (gnus-info-marks info)))
@@ -1851,15 +1851,15 @@ If LIMIT, first try to limit the search to the N last 
articles."
   (setq nnimap-status-string "Read-only server")
   nil)
 
-(defvar gnus-refer-thread-use-nnir) ;; gnus-sum.el
+(defvar gnus-refer-thread-use-search) ;; gnus-sum.el
 (declare-function gnus-fetch-headers "gnus-sum"
                  (articles &optional limit force-new dependencies))
 
-(autoload 'nnir-search-thread "nnir")
+(autoload 'nnselect-search-thread "nnselect")
 
 (deffoo nnimap-request-thread (header &optional group server)
-  (if gnus-refer-thread-use-nnir
-      (nnir-search-thread header)
+  (if gnus-refer-thread-use-search
+      (nnselect-search-thread header)
     (when (nnimap-change-group group server)
       (let* ((cmd (nnimap-make-thread-query header))
              (result (with-current-buffer (nnimap-buffer)
diff --git a/lisp/gnus/nnir.el b/lisp/gnus/nnir.el
index 722969c..2ec39cf 100644
--- a/lisp/gnus/nnir.el
+++ b/lisp/gnus/nnir.el
@@ -10,6 +10,7 @@
 ;; IMAP search improved by Daniel Pittman  <daniel@rimspace.net>.
 ;; nnmaildir support for Swish++ and Namazu backends by:
 ;;   Justus Piater <Justus <at> Piater.name>
+;; Mostly rewritten by Andrew Cohen <cohen@bu.edu> from 2010
 ;; Keywords: news mail searching ir
 
 ;; This file is part of GNU Emacs.
@@ -29,20 +30,11 @@
 
 ;;; Commentary:
 
-;; What does it do?  Well, it allows you to search your mail using
-;; some search engine (imap, namazu, swish-e and others -- see
-;; later) by typing `G G' in the Group buffer.  You will then get a
-;; buffer which shows all articles matching the query, sorted by
-;; Retrieval Status Value (score).
-
-;; When looking at the retrieval result (in the Summary buffer) you
-;; can type `A W' (aka M-x gnus-warp-to-article RET) on an article.  You
-;; will be warped into the group this article came from. Typing `A T'
-;; (aka M-x gnus-summary-refer-thread RET) will warp to the group and
-;; also show the thread this article is part of.
+;; What does it do?  Well, it searches your mail using some search
+;; engine (imap, namazu, swish-e, gmane and others -- see later).
 
 ;; The Lisp setup may involve setting a few variables and setting up the
-;; search engine. You can define the variables in the server definition
+;; search engine.  You can define the variables in the server definition
 ;; like this :
 ;;   (setq gnus-secondary-select-methods '(
 ;;       (nnimap "" (nnimap-address "localhost")
@@ -53,6 +45,45 @@
 ;; an alist, type `C-h v nnir-engines RET' for more information; this
 ;; includes examples for setting `nnir-search-engine', too.)
 
+;; The entry to searching is the single function `nnir-run-query',
+;; which dispatches the search to the proper search function.  The
+;; argument of `nnir-run-query' is an alist with two keys:
+;; 'nnir-query-spec and 'nnir-group-spec.  The value for
+;; 'nnir-query-spec is an alist.  The only required key/value pair is
+;; (query . "query") specifying the search string to pass to the query
+;; engine.  Individual engines may have other elements.  The value of
+;; 'nnir-group-spec is a list with the specification of the
+;; groups/servers to search.  The format of the 'nnir-group-spec is
+;; (("server1" ("group11" "group12")) ("server2" ("group21"
+;; "group22"))). If any of the group lists is absent then all groups
+;; on that server are searched.
+
+;; The output of `nnir-run-query' is a vector, each element of which
+;; should in turn be a three-element vector with the form: [fully
+;; prefixed group-name of the article; the article number; the
+;; Retrieval Status Value (RSV)] as returned from the search engine.
+;; An RSV is the score assigned to the document by the search engine.
+;; For Boolean search engines, the RSV is always 1000 (or 1 or 100, or
+;; whatever you like).
+
+;; A vector of this form is used by the nnselect backend to create
+;; virtual groups.  So nnir-run-query is a suitable function to use in
+;; nnselect groups.
+
+;; The default sorting order of articles in an nnselect summary buffer
+;; is based on the order of the articles in the above mentioned
+;; vector, so that's where you can do the sorting you'd like.  Maybe
+;; it would be nice to have a way of displaying the search result
+;; sorted differently?
+
+;; So what do you need to do when you want to add another search
+;; engine?  You write a function that executes the query.  Temporary
+;; data from the search engine can be put in `nnir-tmp-buffer'.  This
+;; function should return the list of articles as a vector, as
+;; described above.  Then, you need to register this backend in
+;; `nnir-engines'.  Then, users can choose the backend by setting
+;; `nnir-search-engine' as a server variable.
+
 ;; If you use one of the local indices (namazu, find-grep, swish) you
 ;; must also set up a search engine backend.
 
@@ -75,13 +106,13 @@
 ;; ,----
 ;; | package conf;  # Don't remove this line!
 ;; |
-;; | # Paths which will not be indexed. Don't use `^' or `$' anchors.
+;; | # Paths which will not be indexed.  Don't use `^' or `$' anchors.
 ;; | $EXCLUDE_PATH = "spam|sent";
 ;; |
-;; | # Header fields which should be searchable. case-insensitive
+;; | # Header fields which should be searchable.  case-insensitive
 ;; | $REMAIN_HEADER = "from|date|message-id|subject";
 ;; |
-;; | # Searchable fields. case-insensitive
+;; | # Searchable fields.  case-insensitive
 ;; | $SEARCH_FIELD = "from|date|message-id|subject";
 ;; |
 ;; | # The max length of a word.
@@ -121,72 +152,17 @@
 ;; |          (nnml-active-file "~/News/cache/active"))
 ;; `----
 
-;; Developer information:
-
-;; I have tried to make the code expandable.  Basically, it is divided
-;; into two layers.  The upper layer is somewhat like the `nnvirtual'
-;; backend: given a specification of what articles to show from
-;; another backend, it creates a group containing exactly those
-;; articles.  The lower layer issues a query to a search engine and
-;; produces such a specification of what articles to show from the
-;; other backend.
-
-;; The interface between the two layers consists of the single
-;; function `nnir-run-query', which dispatches the search to the
-;; proper search function.  The argument of `nnir-run-query' is an
-;; alist with two keys: 'nnir-query-spec and 'nnir-group-spec. The
-;; value for 'nnir-query-spec is an alist. The only required key/value
-;; pair is (query . "query") specifying the search string to pass to
-;; the query engine. Individual engines may have other elements. The
-;; value of 'nnir-group-spec is a list with the specification of the
-;; groups/servers to search.  The format of the 'nnir-group-spec is
-;; (("server1" ("group11" "group12")) ("server2" ("group21"
-;; "group22"))). If any of the group lists is absent then all groups
-;; on that server are searched.
-
-;; The output of `nnir-run-query' is supposed to be a vector, each
-;; element of which should in turn be a three-element vector.  The
-;; first element should be full group name of the article, the second
-;; element should be the article number, and the third element should
-;; be the Retrieval Status Value (RSV) as returned from the search
-;; engine.  An RSV is the score assigned to the document by the search
-;; engine.  For Boolean search engines, the RSV is always 1000 (or 1
-;; or 100, or whatever you like).
-
-;; The sorting order of the articles in the summary buffer created by
-;; nnir is based on the order of the articles in the above mentioned
-;; vector, so that's where you can do the sorting you'd like.  Maybe
-;; it would be nice to have a way of displaying the search result
-;; sorted differently?
-
-;; So what do you need to do when you want to add another search
-;; engine?  You write a function that executes the query.  Temporary
-;; data from the search engine can be put in `nnir-tmp-buffer'.  This
-;; function should return the list of articles as a vector, as
-;; described above.  Then, you need to register this backend in
-;; `nnir-engines'.  Then, users can choose the backend by setting
-;; `nnir-search-engine' as a server variable.
 
 ;;; Code:
 
 ;;; Setup:
 
-(require 'nnoo)
-(require 'gnus-group)
-(require 'message)
-(require 'gnus-util)
 (eval-when-compile (require 'cl-lib))
+(require 'gnus)
 
 ;;; Internal Variables:
 
-(defvar nnir-memo-query nil
-  "Internal: stores current query.")
-
-(defvar nnir-memo-server nil
-  "Internal: stores current server.")
-
-(defvar nnir-artlist nil
-  "Internal: stores search result.")
+(defvar gnus-inhibit-demon)
 
 (defvar nnir-search-history ()
   "Internal: the history for querying search options in nnir.")
@@ -203,30 +179,19 @@
     ("to" . "TO")
     ("from" . "FROM")
     ("body" . "BODY")
-    ("imap" . ""))
+    ("imap" . "")
+    ("gmail" . "X-GM-RAW"))
   "Mapping from user readable keys to IMAP search items for use in nnir.")
 
 (defvar nnir-imap-search-other "HEADER %S"
-  "The IMAP search item to use for anything other than
-`nnir-imap-search-arguments'.  By default this is the name of an
-email header field.")
+  "The IMAP search item for anything other than `nnir-imap-search-arguments'.
+By default this is the name of an email header field.")
 
 (defvar nnir-imap-search-argument-history ()
   "The history for querying search options in nnir.")
 
 ;;; Helper macros
 
-;; Data type article list.
-
-(defmacro nnir-artlist-length (artlist)
-  "Return number of articles in artlist."
-  `(length ,artlist))
-
-(defmacro nnir-artlist-article (artlist n)
-  "Return from ARTLIST the Nth artitem (counting starting at 1)."
-  `(when (> ,n 0)
-     (elt ,artlist (1- ,n))))
-
 (defmacro nnir-artitem-group (artitem)
   "Return the group from the ARTITEM."
   `(elt ,artitem 0))
@@ -239,52 +204,6 @@ email header field.")
   "Return the Retrieval Status Value (RSV, score) from the ARTITEM."
   `(elt ,artitem 2))
 
-(defmacro nnir-article-group (article)
-  "Return the group for ARTICLE."
-  `(nnir-artitem-group (nnir-artlist-article nnir-artlist ,article)))
-
-(defmacro nnir-article-number (article)
-  "Return the number for ARTICLE."
-  `(nnir-artitem-number (nnir-artlist-article nnir-artlist ,article)))
-
-(defmacro nnir-article-rsv (article)
-  "Return the rsv for ARTICLE."
-  `(nnir-artitem-rsv (nnir-artlist-article nnir-artlist ,article)))
-
-(defsubst nnir-article-ids (article)
-  "Return the pair `(nnir id . real id)' of ARTICLE."
-  (cons article (nnir-article-number article)))
-
-(defmacro nnir-categorize (sequence keyfunc &optional valuefunc)
-  "Sort a SEQUENCE into categories and returns a list of the form
-`((key1 (element11 element12)) (key2 (element21 element22))'.
-The category key for a member of the sequence is obtained
-as `(KEYFUNC member)' and the corresponding element is just
-`member'.  If VALUEFUNC is non-nil, the element of the list
-is `(VALUEFUNC member)'."
-  `(unless (null ,sequence)
-     (let (value)
-       (mapc
-       (lambda (member)
-         (let ((y (,keyfunc member))
-               (x ,(if valuefunc
-                       `(,valuefunc member)
-                     'member)))
-           (if (assoc y value)
-               (push x (cadr (assoc y value)))
-             (push (list y (list x)) value))))
-       ,sequence)
-       value)))
-
-;;; Finish setup:
-
-(require 'gnus-sum)
-
-(nnoo-declare nnir)
-(nnoo-define-basics nnir)
-
-(gnus-declare-backend "nnir" 'mail 'virtual)
-
 
 ;;; User Customizable Variables:
 
@@ -293,43 +212,17 @@ is `(VALUEFUNC member)'."
   :group 'gnus)
 
 (defcustom nnir-ignored-newsgroups ""
-  "A regexp to match newsgroups in the active file that should
-be skipped when searching."
+  "Newsgroups to skip when searching.
+Any newsgroup in the active file matching this regexp will be
+skipped when searching."
   :version "24.1"
   :type '(regexp)
   :group 'nnir)
 
-(defcustom nnir-summary-line-format nil
-  "The format specification of the lines in an nnir summary buffer.
-
-All the items from `gnus-summary-line-format' are available, along
-with three items unique to nnir summary buffers:
-
-%Z    Search retrieval score value (integer)
-%G    Article original full group name (string)
-%g    Article original short group name (string)
-
-If nil this will use `gnus-summary-line-format'."
-  :version "24.1"
-  :type '(choice (const :tag "gnus-summary-line-format" nil) string)
-  :group 'nnir)
-
-(defcustom nnir-retrieve-headers-override-function nil
-  "If non-nil, a function that accepts an article list and group
-and populates the `nntp-server-buffer' with the retrieved
-headers.  Must return either `nov' or `headers' indicating the
-retrieved header format.
-
-If this variable is nil, or if the provided function returns nil for
-a search result, `gnus-retrieve-headers' will be called instead."
-  :version "24.1"
-  :type '(choice (const :tag "gnus-retrieve-headers" nil) function)
-  :group 'nnir)
-
 (defcustom nnir-imap-default-search-key "whole message"
-  "The default IMAP search key for an nnir search.  Must be one of
-the keys in `nnir-imap-search-arguments'.  To use raw imap queries
-by default set this to \"imap\"."
+  "The default IMAP search key for an nnir search.
+Must be one of the keys in `nnir-imap-search-arguments'.  To use
+raw imap queries by default set this to \"imap\"."
   :version "24.1"
   :type `(choice ,@(mapcar (lambda (elem) (list 'const (car elem)))
                           nnir-imap-search-arguments))
@@ -357,9 +250,9 @@ Instead, use this:
   :group 'nnir)
 
 (defcustom nnir-swish++-remove-prefix (concat (getenv "HOME") "/Mail/")
-  "The prefix to remove from each file name returned by swish++
-in order to get a group name (albeit with / instead of .).  This is a
-regular expression.
+  "The prefix to remove from swish++ file names to get group names.
+Resulting names have '/' in place of '.'.  This is a regular
+expression.
 
 This variable is very similar to `nnir-namazu-remove-prefix', except
 that it is for swish++, not Namazu."
@@ -408,9 +301,9 @@ This could be a server parameter."
   :group 'nnir)
 
 (defcustom nnir-swish-e-remove-prefix (concat (getenv "HOME") "/Mail/")
-  "The prefix to remove from each file name returned by swish-e
-in order to get a group name (albeit with / instead of .).  This is a
-regular expression.
+  "The prefix to remove from swish-e file names to get group names.
+Resulting names have '/' in place of '.'.  This is a regular
+expression.
 
 This variable is very similar to `nnir-namazu-remove-prefix', except
 that it is for swish-e, not Namazu.
@@ -441,8 +334,8 @@ Instead, use this:
   :group 'nnir)
 
 (defcustom nnir-hyrex-remove-prefix (concat (getenv "HOME") "/Mail/")
-  "The prefix to remove from each file name returned by HyREX
-in order to get a group name (albeit with / instead of .).
+  "The prefix to remove from HyREX file names to get group names.
+Restulting names have '/' in place  of '.'.
 
 For example, suppose that HyREX returns file names such as
 \"/home/john/Mail/mail/misc/42\".  For this example, use the following
@@ -478,8 +371,8 @@ Instead, use this:
   :group 'nnir)
 
 (defcustom nnir-namazu-remove-prefix (concat (getenv "HOME") "/Mail/")
-  "The prefix to remove from each file name returned by Namazu
-in order to get a group name (albeit with / instead of .).
+  "The prefix to remove from Namazu file names to get group names.
+Resulting names have '/' in place of '.'.
 
 For example, suppose that Namazu returns file names such as
 \"/home/john/Mail/mail/misc/42\".  For this example, use the following
@@ -509,9 +402,9 @@ Instead, use this:
 
 (defcustom nnir-notmuch-remove-prefix
   (regexp-quote (or (getenv "MAILDIR") (expand-file-name "~/Mail")))
-  "The prefix to remove from each file name returned by notmuch
-in order to get a group name (albeit with / instead of .).  This is a
-regular expression.
+  "The prefix to remove from notmuch file names to get group names.
+Resulting names have '/' in place of '.'.  This is a regular
+expression.
 
 This variable is very similar to `nnir-namazu-remove-prefix', except
 that it is for notmuch, not Namazu."
@@ -590,347 +483,12 @@ Add an entry here when adding a new search engine.")
                        ,@(mapcar (lambda (elem) (list 'const (car elem)))
                                  nnir-engines)))))
 
-;; Gnus glue.
-
-(declare-function gnus-group-topic-name "gnus-topic" ())
-(declare-function gnus-topic-find-groups "gnus-topic"
-                 (topic &optional level all lowest recursive))
-
-(defun gnus-group-make-nnir-group (nnir-extra-parms &optional specs)
-  "Create an nnir group.
-Prompt for a search query and determine the groups to search as
-follows: if called from the *Server* buffer search all groups
-belonging to the server on the current line; if called from the
-*Group* buffer search any marked groups, or the group on the current
-line, or all the groups under the current topic.  Calling with a
-prefix-arg prompts for additional search-engine specific constraints.
-A non-nil `specs' arg must be an alist with `nnir-query-spec' and
-`nnir-group-spec' keys, and skips all prompting."
-  (interactive "P")
-  (let* ((group-spec
-         (or (cdr (assq 'nnir-group-spec specs))
-           (if (gnus-server-server-name)
-               (list (list (gnus-server-server-name)))
-             (nnir-categorize
-              (or gnus-group-marked
-                  (if (gnus-group-group-name)
-                      (list (gnus-group-group-name))
-                    (mapcar (lambda (entry)
-                              (gnus-info-group (cadr entry)))
-                            (gnus-topic-find-groups (gnus-group-topic-name)
-                                                    nil t nil t))))
-              gnus-group-server))))
-        (query-spec
-         (or (cdr (assq 'nnir-query-spec specs))
-           (apply
-            'append
-            (list (cons 'query
-                        (read-string "Query: " nil 'nnir-search-history)))
-            (when nnir-extra-parms
-              (mapcar
-               (lambda (x)
-                 (nnir-read-parms (nnir-server-to-search-engine (car x))))
-               group-spec))))))
-    (gnus-group-read-ephemeral-group
-     (concat "nnir-" (message-unique-id))
-     (list 'nnir "nnir")
-     nil
-;     (cons (current-buffer) gnus-current-window-configuration)
-     nil
-     nil nil
-     (list
-      (cons 'nnir-specs (list (cons 'nnir-query-spec query-spec)
-                             (cons 'nnir-group-spec group-spec)))
-      (cons 'nnir-artlist nil)))))
-
-(defun gnus-summary-make-nnir-group (nnir-extra-parms)
-  "Search a group from the summary buffer."
-  (interactive "P")
-  (gnus-warp-to-article)
-  (let ((spec
-        (list
-         (cons 'nnir-group-spec
-               (list (list
-                      (gnus-group-server gnus-newsgroup-name)
-                      (list gnus-newsgroup-name)))))))
-    (gnus-group-make-nnir-group nnir-extra-parms spec)))
-
-
-;; Gnus backend interface functions.
-
-(deffoo nnir-open-server (server &optional definitions)
-  ;; Just set the server variables appropriately.
-  (let ((backend (car (gnus-server-to-method server))))
-    (if backend
-       (nnoo-change-server backend server definitions)
-      (add-hook 'gnus-summary-generate-hook 'nnir-mode)
-      (nnoo-change-server 'nnir server definitions))))
-
-(deffoo nnir-request-group (group &optional server dont-check _info)
-  (nnir-possibly-change-group group server)
-  (let ((pgroup (gnus-group-guess-full-name-from-command-method group))
-       length)
-    ;; Check for cached search result or run the query and cache the
-    ;; result.
-    (unless (and nnir-artlist dont-check)
-      (gnus-group-set-parameter
-       pgroup 'nnir-artlist
-       (setq nnir-artlist
-            (nnir-run-query
-             (gnus-group-get-parameter pgroup 'nnir-specs t))))
-      (nnir-request-update-info pgroup (gnus-get-info pgroup)))
-    (with-current-buffer nntp-server-buffer
-      (if (zerop (setq length (nnir-artlist-length nnir-artlist)))
-          (progn
-            (nnir-close-group group)
-            (nnheader-report 'nnir "Search produced empty results."))
-        (nnheader-insert "211 %d %d %d %s\n"
-                         length    ; total #
-                         1         ; first #
-                         length    ; last #
-                         group)))) ; group name
-  nnir-artlist)
-
-(defvar gnus-inhibit-demon)
-
-(deffoo nnir-retrieve-headers (articles &optional _group _server _fetch-old)
-  (with-current-buffer nntp-server-buffer
-    (let ((gnus-inhibit-demon t)
-         (articles-by-group (nnir-categorize
-                             articles nnir-article-group nnir-article-ids))
-         headers)
-      (while (not (null articles-by-group))
-       (let* ((group-articles (pop articles-by-group))
-              (artgroup (car group-articles))
-              (articleids (cadr group-articles))
-              (artlist (sort (mapcar 'cdr articleids) '<))
-              (server (gnus-group-server artgroup))
-              (gnus-override-method (gnus-server-to-method server))
-              parsefunc)
-         ;; (nnir-possibly-change-group nil server)
-         (erase-buffer)
-         (pcase (setq gnus-headers-retrieved-by
-                       (or
-                        (and
-                         nnir-retrieve-headers-override-function
-                         (funcall nnir-retrieve-headers-override-function
-                                  artlist artgroup))
-                        (gnus-retrieve-headers artlist artgroup nil)))
-           ('nov
-            (setq parsefunc 'nnheader-parse-nov))
-           ('headers
-            (setq parsefunc 'nnheader-parse-head))
-           (_ (error "Unknown header type %s while requesting articles \
-                    of group %s" gnus-headers-retrieved-by artgroup)))
-         (goto-char (point-min))
-         (while (not (eobp))
-           (let* ((novitem (funcall parsefunc))
-                  (artno (and novitem
-                              (mail-header-number novitem)))
-                  (art (car (rassq artno articleids))))
-             (when art
-               (setf (mail-header-number novitem) art)
-               (push novitem headers))
-             (forward-line 1)))))
-      (setq headers
-           (sort headers
-                 (lambda (x y)
-                   (< (mail-header-number x) (mail-header-number y)))))
-      (erase-buffer)
-      (mapc 'nnheader-insert-nov headers)
-      'nov)))
-
-(defvar gnus-article-decode-hook)
-
-(deffoo nnir-request-article (article &optional group server to-buffer)
-  (nnir-possibly-change-group group server)
-  (if (and (stringp article)
-          (not (eq 'nnimap (car (gnus-server-to-method server)))))
-      (nnheader-report
-       'nnir
-       "nnir-request-article only groks message ids for nnimap servers: %s"
-       server)
-    (save-excursion
-      (let ((article article)
-            query)
-        (when (stringp article)
-          (setq gnus-override-method (gnus-server-to-method server))
-          (setq query
-                (list
-                 (cons 'query (format "HEADER Message-ID %s" article))
-                 (cons 'criteria "")
-                 (cons 'shortcut t)))
-          (unless (and nnir-artlist (equal query nnir-memo-query)
-                       (equal server nnir-memo-server))
-            (setq nnir-artlist (nnir-run-imap query server)
-                 nnir-memo-query query
-                 nnir-memo-server server))
-          (setq article 1))
-        (unless (zerop (nnir-artlist-length nnir-artlist))
-          (let ((artfullgroup (nnir-article-group article))
-                (artno (nnir-article-number article)))
-            (message "Requesting article %d from group %s"
-                     artno artfullgroup)
-            (if to-buffer
-                (with-current-buffer to-buffer
-                  (let ((gnus-article-decode-hook nil))
-                    (gnus-request-article-this-buffer artno artfullgroup)))
-              (gnus-request-article artno artfullgroup))
-            (cons artfullgroup artno)))))))
-
-(deffoo nnir-request-move-article (article group server accept-form
-                                          &optional last _internal-move-group)
-  (nnir-possibly-change-group group server)
-  (let* ((artfullgroup (nnir-article-group article))
-        (artno (nnir-article-number article))
-        (to-newsgroup (nth 1 accept-form))
-        (to-method (gnus-find-method-for-group to-newsgroup))
-        (from-method (gnus-find-method-for-group artfullgroup))
-        (move-is-internal (gnus-server-equal from-method to-method)))
-    (unless (gnus-check-backend-function
-            'request-move-article artfullgroup)
-      (error "The group %s does not support article moving" artfullgroup))
-    (gnus-request-move-article
-     artno
-     artfullgroup
-     (nth 1 from-method)
-     accept-form
-     last
-     (and move-is-internal
-         to-newsgroup          ; Not respooling
-         (gnus-group-real-name to-newsgroup)))))
-
-(deffoo nnir-request-expire-articles (articles group &optional server force)
-  (nnir-possibly-change-group group server)
-  (if force
-    (let ((articles-by-group (nnir-categorize
-                             articles nnir-article-group nnir-article-ids))
-         not-deleted)
-      (while (not (null articles-by-group))
-       (let* ((group-articles (pop articles-by-group))
-              (artgroup (car group-articles))
-              (articleids (cadr group-articles))
-              (artlist (sort (mapcar 'cdr articleids) '<)))
-         (unless (gnus-check-backend-function 'request-expire-articles
-                                              artgroup)
-           (error "The group %s does not support article deletion" artgroup))
-         (unless (gnus-check-server (gnus-find-method-for-group artgroup))
-           (error "Couldn't open server for group %s" artgroup))
-         (push (gnus-request-expire-articles
-                artlist artgroup force)
-               not-deleted)))
-      (sort (delq nil not-deleted) '<))
-    articles))
-
-(deffoo nnir-warp-to-article ()
-  (nnir-possibly-change-group gnus-newsgroup-name)
-  (let* ((cur (if (> (gnus-summary-article-number) 0)
-                 (gnus-summary-article-number)
-               (error "Can't warp to a pseudo-article")))
-        (backend-article-group (nnir-article-group cur))
-         (backend-article-number (nnir-article-number cur))
-;       (quit-config (gnus-ephemeral-group-p gnus-newsgroup-name))
-        )
-
-    ;; what should we do here? we could leave all the buffers around
-    ;; and assume that we have to exit from them one by one. or we can
-    ;; try to clean up directly
-
-    ;;first exit from the nnir summary buffer.
-;    (gnus-summary-exit)
-    ;; and if the nnir summary buffer in turn came from another
-    ;; summary buffer we have to clean that summary up too.
- ;   (when (not (eq (cdr quit-config) 'group))
-;      (gnus-summary-exit))
-    (gnus-summary-read-group-1 backend-article-group t t  nil
-                               nil (list backend-article-number))))
-
-(deffoo nnir-request-update-mark (_group article mark)
-  (let ((artgroup (nnir-article-group article))
-       (artnumber (nnir-article-number article)))
-    (or (and artgroup
-            artnumber
-            (gnus-request-update-mark artgroup artnumber mark))
-       mark)))
-
-(deffoo nnir-request-set-mark (group actions &optional server)
-  (nnir-possibly-change-group group server)
-  (let (mlist)
-    (dolist (action actions)
-      (cl-destructuring-bind (range action marks) action
-        (let ((articles-by-group (nnir-categorize
-                                  (gnus-uncompress-range range)
-                                  nnir-article-group nnir-article-number)))
-          (dolist (artgroup articles-by-group)
-            (push (list
-                  (car artgroup)
-                  (list (gnus-compress-sequence
-                         (sort (cadr artgroup) '<))
-                         action marks))
-                  mlist)))))
-    (dolist (request (nnir-categorize  mlist car cadr))
-      (gnus-request-set-mark (car request) (cadr request)))))
-
-
-(deffoo nnir-request-update-info (group info &optional server)
-  (nnir-possibly-change-group group server)
-  ;; clear out all existing marks.
-  (setf (gnus-info-marks info) nil)
-  (setf (gnus-info-read info) nil)
-  (let ((group (gnus-group-guess-full-name-from-command-method group))
-       (articles-by-group
-        (nnir-categorize
-         (gnus-uncompress-range (cons 1 (nnir-artlist-length nnir-artlist)))
-         nnir-article-group nnir-article-ids)))
-    (gnus-set-active group
-                    (cons 1 (nnir-artlist-length nnir-artlist)))
-    (while (not (null articles-by-group))
-      (let* ((group-articles (pop articles-by-group))
-            (articleids (reverse (cadr group-articles)))
-            (group-info (gnus-get-info (car group-articles)))
-            (marks (gnus-info-marks group-info))
-            (read (gnus-info-read group-info)))
-       (setf (gnus-info-read info)
-             (gnus-add-to-range
-              (gnus-info-read info)
-              (delq nil
-                    (mapcar
-                     #'(lambda (art)
-                         (when (gnus-member-of-range (cdr art) read)
-                           (car art)))
-                     articleids))))
-       (dolist (mark marks)
-         (cl-destructuring-bind (type . range) mark
-           (gnus-add-marked-articles
-            group type
-            (delq nil
-                    (mapcar
-                     #'(lambda (art)
-                       (when (gnus-member-of-range (cdr art) range) (car art)))
-                     articleids)))))))))
-
-
-(deffoo nnir-close-group (group &optional server)
-  (nnir-possibly-change-group group server)
-  (let ((pgroup (gnus-group-guess-full-name-from-command-method group)))
-    (when (and nnir-artlist (not (gnus-ephemeral-group-p pgroup)))
-      (gnus-group-set-parameter  pgroup 'nnir-artlist nnir-artlist))
-    (setq nnir-artlist nil)
-    (when (gnus-ephemeral-group-p pgroup)
-      (gnus-kill-ephemeral-group pgroup)
-      (setq gnus-ephemeral-servers
-           (delq (assq 'nnir gnus-ephemeral-servers)
-                 gnus-ephemeral-servers)))))
-;; (gnus-opened-servers-remove
-;;  (car (assoc  '(nnir "nnir-ephemeral" (nnir-address "nnir"))
-;;             gnus-opened-servers))))
-
-
-
 
 (defmacro nnir-add-result (dirnam artno score prefix server artlist)
-  "Ask `nnir-compose-result' to construct a result vector,
-and if it is non-nil, add it to ARTLIST."
+  "Construct a result vector and add it to ARTLIST.
+DIRNAM, ARTNO, SCORE, PREFIX and SERVER are passed to
+`nnir-compose-result' to make the vector.  Only add the result if
+non-nil."
   `(let ((result (nnir-compose-result ,dirnam ,artno ,score ,prefix ,server)))
      (when (not (null result))
        (push result ,artlist))))
@@ -940,9 +498,9 @@ and if it is non-nil, add it to ARTLIST."
 ;; Helper function currently used by the Swish++ and Namazu backends;
 ;; perhaps useful for other backends as well
 (defun nnir-compose-result (dirnam article score prefix server)
-  "Extract the group from DIRNAM, and create a result vector
-ready to be added to the list of search results."
-
+  "Construct a result vector.
+The DIRNAM, ARTICLE, SCORE, PREFIX, and SERVER are used to
+construct the vector entries."
   ;; remove nnir-*-remove-prefix from beginning of dirnam filename
   (when (string-match (concat "^" prefix) dirnam)
     (setq dirnam (replace-match "" t t dirnam)))
@@ -977,13 +535,14 @@ ready to be added to the list of search results."
 
 ;; imap interface
 (defun nnir-run-imap (query srv &optional groups)
-  "Run a search against an IMAP back-end server.
-This uses a custom query language parser; see `nnir-imap-make-query'
-for details on the language and supported extensions."
+  "Run the QUERY search against an IMAP back-end server SRV.
+Search GROUPS, or all active groups on SRV if GROUPS is nil.
+This uses a custom query language parser; see
+`nnir-imap-make-query' for details on the language and supported
+extensions."
   (save-excursion
     (let ((qstring (cdr (assq 'query query)))
           (server (cadr (gnus-server-to-method srv)))
-;;          (defs (nth 2 (gnus-server-to-method srv)))
           (criteria (or (cdr (assq 'criteria query))
                         (cdr (assoc nnir-imap-default-search-key
                                     nnir-imap-search-arguments))))
@@ -995,38 +554,37 @@ for details on the language and supported extensions."
        (catch 'found
          (mapcar
           #'(lambda (group)
-            (let (artlist)
-              (condition-case ()
-                  (when (nnimap-change-group
-                         (gnus-group-short-name group) server)
-                    (with-current-buffer (nnimap-buffer)
-                      (message "Searching %s..." group)
-                      (let ((arts 0)
-                            (result (nnimap-command "UID SEARCH %s"
-                                                    (if (string= criteria "")
-                                                        qstring
-                                                      (nnir-imap-make-query
-                                                       criteria qstring)))))
-                        (mapc
-                         (lambda (artnum)
-                           (let ((artn (string-to-number artnum)))
-                             (when (> artn 0)
-                               (push (vector group artn 100)
-                                     artlist)
-                               (when (assq 'shortcut query)
-                                 (throw 'found (list artlist)))
-                               (setq arts (1+ arts)))))
-                         (and (car result)
-                             (cdr (assoc "SEARCH" (cdr result)))))
-                        (message "Searching %s... %d matches" group arts)))
-                    (message "Searching %s...done" group))
-                (quit nil))
-              (nreverse artlist)))
+             (let (artlist)
+               (condition-case ()
+                   (when (nnimap-change-group
+                          (gnus-group-short-name group) server)
+                     (with-current-buffer (nnimap-buffer)
+                       (message "Searching %s..." group)
+                       (let ((arts 0)
+                             (result (nnimap-command "UID SEARCH %s"
+                                                     (if (string= criteria "")
+                                                         qstring
+                                                       (nnir-imap-make-query
+                                                        criteria qstring)))))
+                         (mapc
+                          (lambda (artnum)
+                            (let ((artn (string-to-number artnum)))
+                              (when (> artn 0)
+                                (push (vector group artn 100)
+                                      artlist)
+                                (when (assq 'shortcut query)
+                                  (throw 'found (list artlist)))
+                                (setq arts (1+ arts)))))
+                          (and (car result)
+                               (cdr (assoc "SEARCH" (cdr result)))))
+                         (message "Searching %s... %d matches" group arts)))
+                     (message "Searching %s...done" group))
+                 (quit nil))
+               (nreverse artlist)))
           groups))))))
 
 (defun nnir-imap-make-query (criteria qstring)
-  "Parse the query string and criteria into an appropriate IMAP search
-expression, returning the string query to make.
+  "Make an IMAP search expression from QSTRING and CRITERIA.
 
 This implements a little language designed to return the expected
 results to an arbitrary query string to the end user.
@@ -1063,7 +621,7 @@ In the future the following will be added to the language:
 
 
 (defun nnir-imap-query-to-imap (criteria query)
-  "Turn an s-expression format QUERY into IMAP."
+  "Turn an s-expression format QUERY with CRITERIA into IMAP."
   (mapconcat
    ;; Turn the expressions into IMAP text
    (lambda (item)
@@ -1099,8 +657,9 @@ In the future the following will be added to the language:
 
 
 (defun nnir-imap-parse-query (string)
-  "Turn STRING into an s-expression based query based on the IMAP
-query language as defined in `nnir-imap-make-query'.
+  "Turn STRING into an s-expression query.
+STRING is based on the IMAP query language as defined in
+`nnir-imap-make-query'.
 
 This involves turning individual tokens into higher level terms
 that the search language can then understand and use."
@@ -1116,7 +675,7 @@ that the search language can then understand and use."
 
 
 (defun nnir-imap-next-expr (&optional count)
-  "Return the next expression from the current buffer."
+  "Return the next (COUNT) expression from the current buffer."
   (let ((term (nnir-imap-next-term count))
        (next (nnir-imap-peek-symbol)))
     ;; Are we looking at an 'or' expression?
@@ -1129,7 +688,7 @@ that the search language can then understand and use."
 
 
 (defun nnir-imap-next-term (&optional count)
-  "Return the next term from the current buffer."
+  "Return the next (COUNT) term from the current buffer."
   (let ((term (nnir-imap-next-symbol count)))
     ;; What sort of term is this?
     (cond
@@ -1147,9 +706,10 @@ that the search language can then understand and use."
     (nnir-imap-next-symbol)))
 
 (defun nnir-imap-next-symbol (&optional count)
-  "Return the next symbol from the current buffer, or nil if we are
-at the end of the buffer.  If supplied COUNT skips some symbols before
-returning the one at the supplied position."
+  "Return the next (COUNT) symbol from the current buffer.
+Return nil if we are at the end of the buffer.  If supplied COUNT
+skips some symbols before returning the one at the supplied
+position."
   (when (and (numberp count) (> count 1))
     (nnir-imap-next-symbol (1- count)))
   (let ((case-fold-search t))
@@ -1180,7 +740,7 @@ returning the one at the supplied position."
            (buffer-substring start end)))))))
 
 (defun nnir-imap-delimited-string (delimiter)
-  "Return a delimited string from the current buffer."
+  "Return a string delimited by DELIMITER from the current buffer."
   (let ((start (point)) end)
     (forward-char 1)                   ; skip the first delimiter.
     (while (not end)
@@ -1207,7 +767,7 @@ returning the one at the supplied position."
 ;; - file size
 ;; - group
 (defun nnir-run-swish++ (query server &optional _group)
-  "Run QUERY against swish++.
+  "Run QUERY on SERVER against swish++.
 Returns a vector of (group name, file name) pairs (also vectors,
 actually).
 
@@ -1297,7 +857,7 @@ Windows NT 4.0."
 
 ;; Swish-E interface.
 (defun nnir-run-swish-e (query server &optional _group)
-  "Run given QUERY against swish-e.
+  "Run given QUERY on SERVER against swish-e.
 Returns a vector of (group name, file name) pairs (also vectors,
 actually).
 
@@ -1392,6 +952,7 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
 
 ;; HyREX interface
 (defun nnir-run-hyrex (query server &optional group)
+  "Run given QUERY with GROUP on SERVER against hyrex."
   (save-excursion
     (let ((artlist nil)
           (groupspec (cdr (assq 'hyrex-group query)))
@@ -1463,7 +1024,7 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
 
 ;; Namazu interface
 (defun nnir-run-namazu (query server &optional _group)
-  "Run given QUERY against Namazu.
+  "Run QUERY on SERVER  against Namazu.
 Returns a vector of (group name, file name) pairs (also vectors,
 actually).
 
@@ -1533,7 +1094,7 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
                                   (nnir-artitem-rsv y)))))))))
 
 (defun nnir-run-notmuch (query server &optional groups)
-  "Run QUERY against notmuch.
+  "Run QUERY with  GROUPS from SERVER  against notmuch.
 Returns a vector of (group name, file name) pairs (also vectors,
 actually).  If GROUPS is a list of group names, use them to
 construct path: search terms (see the variable
@@ -1617,7 +1178,7 @@ construct path: search terms (see the variable
       artlist)))
 
 (defun nnir-run-find-grep (query server &optional grouplist)
-  "Run find and grep to obtain matching articles."
+  "Run find and grep to QUERY GROUPLIST on SERVER for matching articles."
   (let* ((method (gnus-server-to-method server))
         (sym (intern
               (concat (symbol-name (car method)) "-directory")))
@@ -1703,14 +1264,10 @@ construct path: search terms (see the variable
 
 ;;; Util Code:
 
-(defun gnus-nnir-group-p (group)
-  "Say whether GROUP is nnir or not."
-  (if (gnus-group-prefixed-p group)
-      (eq 'nnir (car (gnus-find-method-for-group group)))
-    (and group (string-match "^nnir" group))))
 
 (defun nnir-read-parms (nnir-search-engine)
-  "Read additional search parameters according to `nnir-engines'."
+  "Read additional search parameters for NNIR-SEARCH-ENGINE.
+Parameters are according to `nnir-engines'."
   (let ((parmspec (nth 2 (assoc nnir-search-engine nnir-engines))))
     (mapcar #'nnir-read-parm parmspec)))
 
@@ -1727,7 +1284,7 @@ PARMSPEC is a cons cell, the car is a symbol, the cdr is 
a prompt."
       (cons sym (read-string prompt)))))
 
 (defun nnir-run-query (specs)
-  "Invoke appropriate search engine function (see `nnir-engines')."
+  "Invoke search engine appropriate for SPECS (see `nnir-engines')."
   (apply #'vconcat
         (mapcar
          (lambda (x)
@@ -1736,10 +1293,11 @@ PARMSPEC is a cons cell, the car is a symbol, the cdr 
is a prompt."
                   (search-func (cadr (assoc search-engine nnir-engines))))
              (and search-func
                   (funcall search-func (cdr (assq 'nnir-query-spec specs))
-                           server (cadr x)))))
+                           server (cdr x)))))
          (cdr (assq 'nnir-group-spec specs)))))
 
 (defun nnir-server-to-search-engine (server)
+  "Find search engine for SERVER."
   (or (nnir-read-server-parm 'nnir-search-engine server t)
       (cdr (assoc (car (gnus-server-to-method server))
                  nnir-method-default-engines))))
@@ -1754,48 +1312,10 @@ environment unless NOT-GLOBAL is non-nil."
           ((and (not not-global) (boundp key)) (symbol-value key))
           (t nil))))
 
-(defun nnir-possibly-change-group (group &optional server)
-  (or (not server) (nnir-server-opened server) (nnir-open-server server))
-  (when (gnus-nnir-group-p group)
-    (setq nnir-artlist (gnus-group-get-parameter
-                       (gnus-group-prefixed-name
-                        (gnus-group-short-name group) '(nnir "nnir"))
-                       'nnir-artlist t))))
-
-(defun nnir-server-opened (&optional server)
-  (let ((backend (car (gnus-server-to-method server))))
-    (nnoo-current-server-p (or backend 'nnir) server)))
-
-(autoload 'nnimap-make-thread-query "nnimap")
-(declare-function gnus-registry-get-id-key "gnus-registry" (id key))
-
-(defun nnir-search-thread (header)
-  "Make an nnir group based on the thread containing the article HEADER.
-The current server will be searched.  If the registry is installed,
-the server that the registry reports the current article came from
-is also searched."
-  (let* ((query
-         (list (cons 'query (nnimap-make-thread-query header))
-               (cons 'criteria "")))
-        (server
-         (list (list (gnus-method-to-server
-          (gnus-find-method-for-group gnus-newsgroup-name)))))
-        (registry-group (and
-                         (bound-and-true-p gnus-registry-enabled)
-                         (car (gnus-registry-get-id-key
-                               (mail-header-id header) 'group))))
-        (registry-server
-         (and registry-group
-              (gnus-method-to-server
-               (gnus-find-method-for-group registry-group)))))
-    (when registry-server
-      (cl-pushnew (list registry-server) server :test #'equal))
-    (gnus-group-make-nnir-group nil (list
-                                    (cons 'nnir-query-spec query)
-                                    (cons 'nnir-group-spec server)))
-    (gnus-summary-goto-subject (gnus-id-to-article (mail-header-id header)))))
+(autoload 'gnus-request-list "gnus-int")
 
 (defun nnir-get-active (srv)
+  "Return the active list for SRV."
   (let ((method (gnus-server-to-method srv))
        groups)
     (gnus-request-list method)
@@ -1835,82 +1355,37 @@ is also searched."
            (forward-line)))))
     groups))
 
-;; Behind gnus-registry-enabled test.
-(declare-function gnus-registry-action "gnus-registry"
-                  (action data-header from &optional to method))
-
-(defun nnir-registry-action (action data-header _from &optional to method)
-  "Call `gnus-registry-action' with the original article group."
-  (gnus-registry-action
-   action
-   data-header
-   (nnir-article-group (mail-header-number data-header))
-   to
-   method))
-
-(defun nnir-mode ()
-  (when (eq (car (gnus-find-method-for-group gnus-newsgroup-name)) 'nnir)
-    (when (and nnir-summary-line-format
-               (not (string= nnir-summary-line-format
-                             gnus-summary-line-format)))
-      (setq gnus-summary-line-format nnir-summary-line-format)
-      (gnus-update-format-specifications nil 'summary))
-    (when (bound-and-true-p gnus-registry-enabled)
-      (remove-hook 'gnus-summary-article-delete-hook 'gnus-registry-action t)
-      (remove-hook 'gnus-summary-article-move-hook 'gnus-registry-action t)
-      (remove-hook 'gnus-summary-article-expire-hook 'gnus-registry-action t)
-      (add-hook 'gnus-summary-article-delete-hook 'nnir-registry-action t t)
-      (add-hook 'gnus-summary-article-move-hook 'nnir-registry-action t t)
-      (add-hook 'gnus-summary-article-expire-hook 'nnir-registry-action t t))))
-
-
-(defun gnus-summary-create-nnir-group ()
-  (interactive)
-  (or (nnir-server-opened "") (nnir-open-server "nnir"))
-  (let ((name (gnus-read-group "Group name: "))
-       (method '(nnir ""))
-       (pgroup
-        (gnus-group-guess-full-name-from-command-method gnus-newsgroup-name)))
-    (with-current-buffer gnus-group-buffer
-      (gnus-group-make-group
-       name method nil
-       (gnus-group-find-parameter pgroup)))))
-
-
-(deffoo nnir-request-create-group (group &optional _server args)
-  (message "Creating nnir group %s" group)
-  (let* ((group (gnus-group-prefixed-name  group '(nnir "nnir")))
-         (specs (assq 'nnir-specs args))
-         (query-spec
-          (or (cdr (assq 'nnir-query-spec specs))
-              (list (cons 'query
-                          (read-string "Query: " nil 'nnir-search-history)))))
-         (group-spec
-          (or (cdr (assq 'nnir-group-spec specs))
-              (list (list (read-string "Server: " nil nil)))))
-         (nnir-specs (list (cons 'nnir-query-spec query-spec)
-                           (cons 'nnir-group-spec group-spec))))
-    (gnus-group-set-parameter group 'nnir-specs nnir-specs)
-    (gnus-group-set-parameter
-     group 'nnir-artlist
-     (or (cdr (assq 'nnir-artlist args))
-         (nnir-run-query nnir-specs)))
-    (nnir-request-update-info group (gnus-get-info group)))
-  t)
-
-(deffoo nnir-request-delete-group (_group &optional _force _server)
-  t)
-
-(deffoo nnir-request-list (&optional _server)
-  t)
-
-(deffoo nnir-request-scan (_group _method)
-  t)
-
-(deffoo nnir-request-close ()
-  t)
-
-(nnoo-define-skeleton nnir)
+(autoload 'nnselect-categorize "nnselect" nil nil)
+(autoload 'gnus-group-topic-name "gnus-topic" nil nil)
+(defvar gnus-group-marked)
+(defvar gnus-topic-alist)
+
+(defun nnir-make-specs (nnir-extra-parms &optional specs)
+  "Make the query-spec and group-spec for a search with NNIR-EXTRA-PARMS.
+Query for the specs, or use SPECS."
+  (let* ((group-spec
+         (or (cdr (assq 'nnir-group-spec specs))
+             (if (gnus-server-server-name)
+                 (list (list (gnus-server-server-name)))
+               (nnselect-categorize
+                (or gnus-group-marked
+                    (if (gnus-group-group-name)
+                        (list (gnus-group-group-name))
+                      (cdr (assoc (gnus-group-topic-name) gnus-topic-alist))))
+                'nnselect-group-server))))
+        (query-spec
+         (or (cdr (assq 'nnir-query-spec specs))
+             (apply
+              'append
+              (list (cons 'query
+                          (read-string "Query: " nil 'nnir-search-history)))
+              (when nnir-extra-parms
+                (mapcar
+                 (lambda (x)
+                   (nnir-read-parms (nnir-server-to-search-engine (car x))))
+                 group-spec))))))
+    (list (cons 'nnir-query-spec query-spec)
+         (cons 'nnir-group-spec group-spec))))
 
 ;; The end.
 (provide 'nnir)
diff --git a/lisp/gnus/nnmaildir.el b/lisp/gnus/nnmaildir.el
index 9c7b125..81a148d 100644
--- a/lisp/gnus/nnmaildir.el
+++ b/lisp/gnus/nnmaildir.el
@@ -492,7 +492,7 @@ This variable is set by `nnmaildir-request-article'.")
            (setq nov-mid 0))
          (goto-char (point-min))
          (delete-char 1)
-         (setq nov (nnheader-parse-naked-head)
+         (setq nov (nnheader-parse-head t)
                field (or (mail-header-lines nov) 0)))
        (unless (or (zerop field) (nnmaildir--param pgname 'distrust-Lines:))
          (setq nov-mid field))
diff --git a/lisp/gnus/nnml.el b/lisp/gnus/nnml.el
index baf5d54..ad608b6 100644
--- a/lisp/gnus/nnml.el
+++ b/lisp/gnus/nnml.el
@@ -766,7 +766,7 @@ article number.  This function is called narrowed to an 
article."
         (if (re-search-forward "\n\r?\n" nil t)
             (1- (point))
           (point-max))))
-      (let ((headers (nnheader-parse-naked-head)))
+      (let ((headers (nnheader-parse-head t)))
        (setf (mail-header-chars  headers) chars)
        (setf (mail-header-number headers) number)
        headers))))
diff --git a/lisp/gnus/nnselect.el b/lisp/gnus/nnselect.el
new file mode 100644
index 0000000..460bc63
--- /dev/null
+++ b/lisp/gnus/nnselect.el
@@ -0,0 +1,864 @@
+;;; nnselect.el --- a virtual group backend   -*- lexical-binding:t -*-
+
+;; Copyright (C) 2020 Free Software Foundation, Inc.
+
+;; Author: Andrew Cohen <cohen@andy.bu.edu>
+;; Keywords: news mail
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is a "virtual" backend that allows an aribtrary list of
+;; articles to be treated as a gnus group.  An nnselect group uses an
+;; nnselect-spec group parameter to specify this list of
+;; articles.  nnselect-spec is an alist with two keys:
+;; nnselect-function, whose value should be a function that returns
+;; the list of articles, and nnselect-args.  The function will be
+;; applied to the arguments to generate the list of articles.  The
+;; return value should be a vector, each element of which should in
+;; turn be a vector of three elements: a real prefixed group name, an
+;; article number in that group, and an integer score.  The score is
+;; not used by nnselect but may be used by other code to help in
+;; sorting.  Most functions will just chose a fixed number, such as
+;; 100, for this score.
+
+;; For example the search function `nnir-run-query' applied to
+;; arguments specifying a search query (see "nnir.el") can be used to
+;; return a list of articles from a search.  Or the function can be the
+;; identity and the args a vector of articles.
+
+
+;;; Code:
+
+;;; Setup:
+
+(require 'gnus-art)
+(require 'nnir)
+
+(eval-when-compile (require 'cl-lib))
+
+;; Set up the backend
+
+(nnoo-declare nnselect)
+
+(nnoo-define-basics nnselect)
+
+(gnus-declare-backend "nnselect" 'post-mail 'virtual)
+
+;;; Internal Variables:
+
+(defvar gnus-inhibit-demon)
+(defvar gnus-message-group-art)
+
+;; For future use
+(defvoo nnselect-directory gnus-directory
+  "Directory for the nnselect backend.")
+
+(defvoo nnselect-active-file
+    (expand-file-name "nnselect-active" nnselect-directory)
+  "nnselect active file.")
+
+(defvoo nnselect-groups-file
+    (expand-file-name "nnselect-newsgroups" nnselect-directory)
+  "nnselect groups description file.")
+
+;;; Helper routines.
+(defun nnselect-compress-artlist (artlist)
+  "Compress ARTLIST."
+  (let (selection)
+    (pcase-dolist (`(,artgroup . ,arts)
+                   (nnselect-categorize artlist 'nnselect-artitem-group))
+      (let (list)
+        (pcase-dolist (`(,rsv . ,articles)
+                       (nnselect-categorize
+                        arts 'nnselect-artitem-rsv 'nnselect-artitem-number))
+          (push (cons rsv (gnus-compress-sequence (sort articles '<)))
+                list))
+        (push (cons artgroup list) selection)))
+    selection))
+
+(defun nnselect-uncompress-artlist (artlist)
+  "Uncompress ARTLIST."
+  (if (vectorp artlist)
+      artlist
+    (let (selection)
+      (pcase-dolist (`(,artgroup (,artrsv . ,artseq)) artlist)
+       (setq selection
+             (vconcat
+              (cl-map 'vector
+                   #'(lambda (art)
+                       (vector artgroup art artrsv))
+                   (gnus-uncompress-sequence artseq)) selection)))
+      selection)))
+
+(defun nnselect-group-server (group)
+  "Return the server for GROUP."
+  (gnus-group-server group))
+
+;; Data type article list.
+
+(define-inline nnselect-artlist-length (artlist)
+    (inline-quote (length ,artlist)))
+
+(define-inline nnselect-artlist-article (artlist n)
+  "Return from ARTLIST the Nth artitem (counting starting at 1)."
+  (inline-quote (when (> ,n 0)
+                 (elt ,artlist (1- ,n)))))
+
+(define-inline nnselect-artitem-group (artitem)
+  "Return the group from the ARTITEM."
+  (inline-quote (elt ,artitem 0)))
+
+(define-inline nnselect-artitem-number (artitem)
+  "Return the number from the ARTITEM."
+  (inline-quote (elt ,artitem 1)))
+
+(define-inline nnselect-artitem-rsv (artitem)
+  "Return the Retrieval Status Value (RSV, score) from the ARTITEM."
+  (inline-quote (elt ,artitem 2)))
+
+(define-inline nnselect-article-group (article)
+  "Return the group for ARTICLE."
+  (inline-quote
+   (nnselect-artitem-group  (nnselect-artlist-article
+                            gnus-newsgroup-selection ,article))))
+
+(define-inline nnselect-article-number (article)
+  "Return the number for ARTICLE."
+  (inline-quote (nnselect-artitem-number
+                (nnselect-artlist-article
+                 gnus-newsgroup-selection ,article))))
+
+(define-inline nnselect-article-rsv (article)
+  "Return the rsv for ARTICLE."
+  (inline-quote (nnselect-artitem-rsv
+                (nnselect-artlist-article
+                 gnus-newsgroup-selection ,article))))
+
+(define-inline nnselect-article-id (article)
+  "Return the pair `(nnselect id . real id)' of ARTICLE."
+  (inline-quote (cons ,article (nnselect-article-number ,article))))
+
+(define-inline nnselect-categorize (sequence keyfunc &optional valuefunc)
+  "Sorts a sequence into categories.
+Returns a list of the form
+`((key1 (element11 element12)) (key2 (element21 element22))'.
+The category key for a member of the sequence is obtained
+as `(keyfunc member)' and the corresponding element is just
+`member' (or `(valuefunc member)' if `valuefunc' is non-nil)."
+  (inline-letevals (sequence keyfunc valuefunc)
+    (inline-quote  (let ((valuefunc (or ,valuefunc 'identity))
+                        result)
+                    (unless (null ,sequence)
+                     (mapc
+                      (lambda (member)
+                        (let* ((key (funcall ,keyfunc member))
+                               (value  (funcall valuefunc member))
+                               (kr (assoc key result)))
+                          (if kr
+                              (push value (cdr kr))
+                            (push (list key value) result))))
+                      (reverse ,sequence))
+                     result)))))
+
+
+;; Unclear whether a macro or an inline function is best.
+;; (defmacro nnselect-categorize (sequence keyfunc &optional valuefunc)
+;;   "Sorts a sequence into categories and returns a list of the form
+;; `((key1 (element11 element12)) (key2 (element21 element22))'.
+;; The category key for a member of the sequence is obtained
+;; as `(keyfunc member)' and the corresponding element is just
+;; `member' (or `(valuefunc member)' if `valuefunc' is non-nil)."
+;;   (let ((key (make-symbol "key"))
+;;     (value (make-symbol "value"))
+;;     (result (make-symbol "result"))
+;;     (valuefunc (or valuefunc 'identity)))
+;;     `(unless (null ,sequence)
+;;        (let (,result)
+;;      (mapc
+;;       (lambda (member)
+;;         (let* ((,key (,keyfunc member))
+;;                (,value  (,valuefunc member))
+;;                (kr (assoc ,key ,result)))
+;;           (if kr
+;;               (push ,value (cdr kr))
+;;             (push (list ,key ,value) ,result))))
+;;       (reverse ,sequence))
+;;      ,result))))
+
+(define-inline ids-by-group (articles)
+  (inline-quote
+   (nnselect-categorize ,articles 'nnselect-article-group
+                       'nnselect-article-id)))
+
+(define-inline numbers-by-group (articles)
+  (inline-quote
+   (nnselect-categorize
+    ,articles 'nnselect-article-group 'nnselect-article-number)))
+
+
+(defmacro nnselect-add-prefix (group)
+  "Ensures that the GROUP has an nnselect prefix."
+  `(gnus-group-prefixed-name
+   (gnus-group-short-name ,group) '(nnselect "nnselect")))
+
+(defmacro nnselect-get-artlist (group)
+  "Retrieve the list of articles for GROUP."
+  `(when (gnus-nnselect-group-p ,group)
+     (nnselect-uncompress-artlist
+      (gnus-group-get-parameter ,group 'nnselect-artlist t))))
+
+(defmacro nnselect-add-novitem (novitem)
+  "Add NOVITEM to the list of headers."
+  `(let* ((novitem ,novitem)
+         (artno (and novitem
+                     (mail-header-number novitem)))
+         (art (car-safe (rassq artno artids))))
+     (when art
+       (setf (mail-header-number novitem) art)
+       (push novitem headers))))
+
+;;; User Customizable Variables:
+
+(defgroup nnselect nil
+  "Virtual groups in Gnus with arbitrary selection methods."
+  :group 'gnus)
+
+(defcustom nnselect-retrieve-headers-override-function nil
+  "A function that retrieves article headers for ARTICLES from GROUP.
+The retrieved headers should populate the `nntp-server-buffer'.
+Returns either the retrieved header format 'nov or 'headers.
+
+If this variable is nil, or if the provided function returns nil,
+  `gnus-retrieve-headers' will be called instead."
+  :version "24.1" :type '(function) :group 'nnselect)
+
+
+;; Gnus backend interface functions.
+
+(deffoo nnselect-open-server (server &optional definitions)
+  ;; Just set the server variables appropriately.
+  (let ((backend (or (car (gnus-server-to-method server)) 'nnselect)))
+    (nnoo-change-server backend server definitions)))
+
+;; (deffoo nnselect-server-opened (&optional server)
+;;   "Is SERVER the current virtual server?"
+;;   (if (string-empty-p server)
+;;       t
+;;     (let ((backend (car (gnus-server-to-method server))))
+;;     (nnoo-current-server-p (or backend 'nnselect) server))))
+
+(deffoo nnselect-server-opened (&optional _server)
+  t)
+
+
+(deffoo nnselect-request-group (group &optional _server _dont-check info)
+  (let* ((group (nnselect-add-prefix group))
+        (nnselect-artlist (nnselect-get-artlist group))
+        length)
+    ;; Check for cached select result or run the selection and cache
+    ;; the result.
+    (unless nnselect-artlist
+      (gnus-group-set-parameter
+       group 'nnselect-artlist
+       (nnselect-compress-artlist (setq nnselect-artlist
+            (nnselect-run
+             (gnus-group-get-parameter group 'nnselect-specs t)))))
+      (nnselect-request-update-info
+       group (or info (gnus-get-info group))))
+    (if (zerop (setq length (nnselect-artlist-length nnselect-artlist)))
+       (progn
+         (nnheader-report 'nnselect "Selection produced empty results.")
+         (nnheader-insert ""))
+      (with-current-buffer nntp-server-buffer
+       (nnheader-insert "211 %d %d %d %s\n"
+                         length    ; total #
+                         1         ; first #
+                         length    ; last #
+                         group))) ; group name
+  nnselect-artlist))
+
+
+(deffoo nnselect-retrieve-headers (articles group &optional _server fetch-old)
+  (let ((group (nnselect-add-prefix group)))
+    (with-current-buffer (gnus-summary-buffer-name group)
+      (setq gnus-newsgroup-selection (or gnus-newsgroup-selection
+                                        (nnselect-get-artlist group)))
+      (let ((gnus-inhibit-demon t)
+           (gartids (ids-by-group articles))
+           headers)
+       (with-current-buffer nntp-server-buffer
+         (pcase-dolist (`(,artgroup . ,artids) gartids)
+           (let ((artlist (sort (mapcar 'cdr artids) '<))
+                 (gnus-override-method (gnus-find-method-for-group artgroup))
+                 (fetch-old
+                  (or
+                   (car-safe
+                    (gnus-group-find-parameter artgroup
+                                               'gnus-fetch-old-headers t))
+                   fetch-old)))
+             (erase-buffer)
+             (pcase (setq gnus-headers-retrieved-by
+                          (or
+                           (and
+                            nnselect-retrieve-headers-override-function
+                            (funcall
+                             nnselect-retrieve-headers-override-function
+                             artlist artgroup))
+                           (gnus-retrieve-headers
+                            artlist artgroup fetch-old)))
+               ('nov
+                (goto-char (point-min))
+                (while (not (eobp))
+                  (nnselect-add-novitem
+                   (nnheader-parse-nov))
+                  (forward-line 1)))
+               ('headers
+                (goto-char (point-min))
+                (while (not (eobp))
+                  (nnselect-add-novitem
+                   (nnheader-parse-head))
+                  (forward-line 1)))
+               ((pred listp)
+                (dolist (novitem gnus-headers-retrieved-by)
+                  (nnselect-add-novitem novitem)))
+               (_ (error "Unknown header type %s while requesting articles \
+                    of group %s" gnus-headers-retrieved-by artgroup)))))
+         (setq headers
+               (sort
+                headers
+                (lambda (x y)
+                  (< (mail-header-number x) (mail-header-number y))))))))))
+
+
+(deffoo nnselect-request-article (article &optional _group server to-buffer)
+  (let* ((gnus-override-method nil)
+        servers group-art artlist)
+    (if (numberp article)
+       (with-current-buffer gnus-summary-buffer
+         (unless (zerop (nnselect-artlist-length
+                         gnus-newsgroup-selection))
+           (setq group-art (cons (nnselect-article-group article)
+                                 (nnselect-article-number article)))))
+      ;; message-id: either coming from a referral or a pseudo-article
+      ;; find the servers for a pseudo-article
+      (if (eq 'nnselect (car (gnus-server-to-method server)))
+         (with-current-buffer gnus-summary-buffer
+           (let ((thread  (gnus-id-to-thread article)))
+             (when thread
+               (mapc
+                #'(lambda (x)
+                    (when (and x (> x 0))
+                      (cl-pushnew
+                       (list
+                        (gnus-method-to-server
+                         (gnus-find-method-for-group
+                          (nnselect-article-group x)))) servers :test 'equal)))
+                (gnus-articles-in-thread thread)))))
+       (setq servers (list (list server))))
+      (setq artlist
+           (nnir-run-query
+            (list
+             (cons 'nnir-query-spec
+                   (list (cons 'query  (format "HEADER Message-ID %s" article))
+                   (cons 'criteria "")  (cons 'shortcut t)))
+             (cons 'nnir-group-spec servers))))
+      (unless (zerop (nnselect-artlist-length artlist))
+       (setq
+        group-art
+        (cons
+         (nnselect-artitem-group (nnselect-artlist-article  artlist 1))
+         (nnselect-artitem-number (nnselect-artlist-article  artlist 1))))))
+    (when (numberp (cdr group-art))
+      (message "Requesting article %d from group %s"
+              (cdr group-art) (car group-art))
+      (if to-buffer
+         (with-current-buffer to-buffer
+           (let ((gnus-article-decode-hook nil))
+             (gnus-request-article-this-buffer
+              (cdr group-art) (car group-art))))
+       (gnus-request-article (cdr group-art) (car group-art)))
+      group-art)))
+
+
+(deffoo nnselect-request-move-article
+    (article _group _server accept-form &optional last _internal-move-group)
+  (let* ((artgroup (nnselect-article-group article))
+        (artnumber (nnselect-article-number article))
+        (to-newsgroup (nth 1 accept-form))
+        (to-method (gnus-find-method-for-group to-newsgroup))
+        (from-method (gnus-find-method-for-group artgroup))
+        (move-is-internal (gnus-server-equal from-method to-method)))
+    (unless (gnus-check-backend-function
+            'request-move-article artgroup)
+      (error "The group %s does not support article moving" artgroup))
+    (gnus-request-move-article
+     artnumber
+     artgroup
+     (nth 1 from-method)
+     accept-form
+     last
+     (and move-is-internal
+         to-newsgroup          ; Not respooling
+         (gnus-group-real-name to-newsgroup)))))
+
+
+(deffoo nnselect-request-expire-articles
+    (articles _group &optional _server force)
+  (if force
+      (let (not-expired)
+       (pcase-dolist (`(,artgroup . ,artids) (ids-by-group articles))
+         (let ((artlist (sort (mapcar 'cdr artids) '<)))
+           (unless (gnus-check-backend-function 'request-expire-articles
+                                                artgroup)
+             (error "Group %s does not support article expiration" artgroup))
+           (unless (gnus-check-server (gnus-find-method-for-group artgroup))
+             (error "Couldn't open server for group %s" artgroup))
+           (push (mapcar #'(lambda (art)
+                             (car (rassq art artids)))
+                         (let ((nnimap-expunge 'immediately))
+                           (gnus-request-expire-articles
+                            artlist artgroup force)))
+                 not-expired)))
+       (sort (delq nil not-expired) '<))
+    articles))
+
+
+(deffoo nnselect-warp-to-article ()
+  (let* ((cur (if (> (gnus-summary-article-number) 0)
+                 (gnus-summary-article-number)
+               (error "Can't warp to a pseudo-article")))
+        (artgroup (nnselect-article-group cur))
+         (artnumber (nnselect-article-number cur))
+        (_quit-config (gnus-ephemeral-group-p gnus-newsgroup-name)))
+
+    ;; what should we do here? we could leave all the buffers around
+    ;; and assume that we have to exit from them one by one. or we can
+    ;; try to clean up directly
+
+    ;;first exit from the nnselect summary buffer.
+    ;;(gnus-summary-exit)
+    ;; and if the nnselect summary buffer in turn came from another
+    ;; summary buffer we have to clean that summary up too.
+    ;;(when (not (eq (cdr quit-config) 'group))
+    ;;  (gnus-summary-exit))
+    (gnus-summary-read-group-1 artgroup t t  nil
+                               nil (list artnumber))))
+
+
+;; we pass this through to the real group in case it wants to adjust
+;; the mark. We also use this to mark an article expirable iff it is
+;; expirable in the real group.
+(deffoo nnselect-request-update-mark (_group article mark)
+  (let* ((artgroup (nnselect-article-group article))
+        (artnumber (nnselect-article-number article))
+        (gmark (gnus-request-update-mark artgroup artnumber mark)))
+    (when (and artnumber
+              (memq mark gnus-auto-expirable-marks)
+              (= mark gmark)
+              (gnus-group-auto-expirable-p artgroup))
+      (setq gmark gnus-expirable-mark))
+    gmark))
+
+
+(deffoo nnselect-request-set-mark (_group actions &optional _server)
+  (mapc
+   (lambda (request) (gnus-request-set-mark (car request) (cdr request)))
+   (nnselect-categorize
+    (cl-mapcan
+     (lambda (act)
+       (cl-destructuring-bind (range action marks) act
+        (mapcar
+         (lambda (artgroup)
+           (list (car artgroup)
+                 (gnus-compress-sequence (sort (cdr artgroup) '<))
+                 action marks))
+         (numbers-by-group
+          (gnus-uncompress-range range)))))
+     actions)
+    'car 'cdr)))
+
+(deffoo nnselect-request-update-info (group info &optional _server)
+  (let* ((group  (nnselect-add-prefix group))
+        (gnus-newsgroup-selection (or gnus-newsgroup-selection
+                                      (nnselect-get-artlist group))))
+    (gnus-info-set-marks info nil)
+    (setf (gnus-info-read info) nil)
+    (pcase-dolist (`(,artgroup . ,nartids)
+                  (ids-by-group
+                   (number-sequence 1 (nnselect-artlist-length
+                                       gnus-newsgroup-selection))))
+      (let* ((gnus-newsgroup-active nil)
+            (artids (cl-sort nartids '< :key 'car))
+            (group-info (gnus-get-info artgroup))
+            (marks (gnus-info-marks group-info))
+            (unread (gnus-uncompress-sequence
+                     (gnus-range-difference (gnus-active artgroup)
+                                            (gnus-info-read group-info)))))
+       (gnus-atomic-progn
+         (setf (gnus-info-read info)
+               (gnus-add-to-range
+                (gnus-info-read info)
+                (delq nil
+                      (mapcar
+                       #'(lambda (art)
+                           (unless (memq (cdr art) unread) (car art)))
+                       artids))))
+         (pcase-dolist (`(,type . ,range) marks)
+           (setq range (gnus-uncompress-sequence range))
+           (gnus-add-marked-articles
+            group type
+            (delq nil
+                  (mapcar
+                   #'(lambda (art)
+                       (when (memq (cdr art) range)
+                         (car art)))  artids)))))))
+    (gnus-set-active group (cons 1 (nnselect-artlist-length
+                                   gnus-newsgroup-selection)))))
+
+
+(deffoo nnselect-request-thread (header &optional group server)
+  (with-current-buffer gnus-summary-buffer
+    (let ((group (nnselect-add-prefix group))
+         ;; find the best group for the originating article. if its a
+         ;; pseudo-article look for real articles in the same thread
+         ;; and see where they come from.
+         (artgroup (nnselect-article-group
+                    (if (> (mail-header-number header) 0)
+                        (mail-header-number header)
+                      (if (> (gnus-summary-article-number) 0)
+                          (gnus-summary-article-number)
+                        (let ((thread
+                               (gnus-id-to-thread (mail-header-id header))))
+                          (when thread
+                            (cl-some #'(lambda (x)
+                                         (when (and x (> x 0)) x))
+                                     (gnus-articles-in-thread thread)))))))))
+      ;; Check if we are dealing with an imap backend.
+      (if (eq 'nnimap
+             (car (gnus-find-method-for-group artgroup)))
+         ;; If so we perform the query, massage the result, and return
+         ;; the new headers back to the caller to incorporate into the
+         ;; current summary buffer.
+         (let* ((group-spec
+                 (list (delq nil (list
+                                  (or server (gnus-group-server artgroup))
+                                  (unless  gnus-refer-thread-use-search
+                                    artgroup)))))
+                (query-spec
+                 (list (cons 'query (nnimap-make-thread-query header))
+                       (cons 'criteria "")))
+                (last (nnselect-artlist-length gnus-newsgroup-selection))
+                (first (1+ last))
+                (new-nnselect-artlist
+                 (nnir-run-query
+                  (list (cons 'nnir-query-spec query-spec)
+                        (cons 'nnir-group-spec group-spec))))
+                old-arts seq
+                headers)
+           (mapc
+            #'(lambda (article)
+                (if
+                    (setq seq
+                          (cl-position article
+                                       gnus-newsgroup-selection :test 'equal))
+                    (push (1+ seq) old-arts)
+                  (setq gnus-newsgroup-selection
+                        (vconcat gnus-newsgroup-selection (vector article)))
+                  (cl-incf last)))
+            new-nnselect-artlist)
+           (setq headers
+                 (gnus-fetch-headers
+                  (append (sort old-arts '<)
+                          (number-sequence first last)) nil t))
+           (gnus-group-set-parameter
+            group
+            'nnselect-artlist
+            (nnselect-compress-artlist gnus-newsgroup-selection))
+           (when (>= last first)
+             (let (new-marks)
+               (pcase-dolist (`(,artgroup . ,artids)
+                              (ids-by-group (number-sequence first last)))
+                 (pcase-dolist (`(,type . ,marked)
+                                (gnus-info-marks (gnus-get-info artgroup)))
+                   (setq marked (gnus-uncompress-sequence marked))
+                   (when (setq new-marks
+                               (delq nil
+                                     (mapcar
+                                      #'(lambda (art)
+                                          (when (memq (cdr art) marked)
+                                            (car art)))
+                                      artids)))
+                     (nconc
+                      (symbol-value
+                       (intern
+                        (format "gnus-newsgroup-%s"
+                                (car (rassq type gnus-article-mark-lists)))))
+                      new-marks)))))
+             (setq gnus-newsgroup-active
+                   (cons 1 (nnselect-artlist-length gnus-newsgroup-selection)))
+             (gnus-set-active
+              group
+              (cons 1 (nnselect-artlist-length gnus-newsgroup-selection))))
+           headers)
+       ;; If not an imap backend just warp to the original article
+       ;; group and punt back to gnus-summary-refer-thread.
+       (and (gnus-warp-to-article) (gnus-summary-refer-thread))))))
+
+
+(deffoo nnselect-close-group (group &optional _server)
+  (let ((group (nnselect-add-prefix group)))
+    (unless gnus-group-is-exiting-without-update-p
+      (nnselect-push-info group))
+    (setq gnus-newsgroup-selection nil)
+    (when (gnus-ephemeral-group-p group)
+      (gnus-kill-ephemeral-group group)
+      (setq gnus-ephemeral-servers
+           (assq-delete-all 'nnselect gnus-ephemeral-servers)))))
+
+
+(deffoo nnselect-request-create-group (group &optional _server args)
+  (message "Creating nnselect group %s" group)
+  (let* ((group (gnus-group-prefixed-name  group '(nnselect "nnselect")))
+         (specs (assq 'nnselect-specs args))
+         (function-spec
+          (or  (alist-get 'nnselect-function specs)
+              (intern (completing-read "Function: " obarray #'functionp))))
+         (args-spec
+          (or  (alist-get 'nnselect-args specs)
+               (read-from-minibuffer "Args: " nil nil t nil "nil")))
+         (nnselect-specs (list (cons 'nnselect-function function-spec)
+                              (cons 'nnselect-args args-spec))))
+    (gnus-group-set-parameter group 'nnselect-specs nnselect-specs)
+    (gnus-group-set-parameter
+     group 'nnselect-artlist
+     (nnselect-compress-artlist (or  (alist-get 'nnselect-artlist args)
+         (nnselect-run nnselect-specs))))
+    (nnselect-request-update-info group (gnus-get-info group)))
+  t)
+
+
+(deffoo nnselect-request-type (_group &optional article)
+  (if (and (numberp article) (> article 0))
+      (gnus-request-type
+       (nnselect-article-group article) (nnselect-article-number article))
+    'unknown))
+
+(deffoo nnselect-request-post (&optional _server)
+  (if (not gnus-message-group-art)
+      (nnheader-report 'nnselect "Can't post to an nnselect group")
+    (gnus-request-post
+     (gnus-find-method-for-group
+      (nnselect-article-group (cdr gnus-message-group-art))))))
+
+
+(deffoo nnselect-request-rename-group (_group _new-name &optional _server)
+  t)
+
+
+(deffoo nnselect-request-scan (group _method)
+  (when (and group
+            (gnus-group-get-parameter  (nnselect-add-prefix group)
+                                       'nnselect-rescan t))
+    (nnselect-request-group-scan group)))
+
+
+(deffoo nnselect-request-group-scan (group &optional _server _info)
+  (let* ((group (nnselect-add-prefix group))
+        (artlist (nnselect-run
+                  (gnus-group-get-parameter group 'nnselect-specs t))))
+    (gnus-set-active group (cons 1 (nnselect-artlist-length
+                                   artlist)))
+    (gnus-group-set-parameter
+     group 'nnselect-artlist
+     (nnselect-compress-artlist artlist))))
+
+;; Add any undefined required backend functions
+
+;; (nnoo-define-skeleton nnselect)
+
+;;; Util Code:
+
+(defun gnus-nnselect-group-p (group)
+  "Say whether GROUP is nnselect or not."
+  (or (and (gnus-group-prefixed-p group)
+          (eq 'nnselect (car (gnus-find-method-for-group group))))
+      (eq 'nnselect (car gnus-command-method))))
+
+
+(defun nnselect-run (specs)
+  "Apply nnselect-function to nnselect-args from SPECS.
+Return an article list."
+  (let ((func (alist-get 'nnselect-function specs))
+       (args (alist-get 'nnselect-args specs)))
+    (funcall func args)))
+
+
+(defun nnselect-search-thread (header)
+  "Make an nnselect group containing the thread with article HEADER.
+The current server will be searched.  If the registry is
+installed, the server that the registry reports the current
+article came from is also searched."
+  (let* ((query
+         (list (cons 'query (nnimap-make-thread-query header))
+               (cons 'criteria "")))
+        (server
+         (list (list (gnus-method-to-server
+          (gnus-find-method-for-group gnus-newsgroup-name)))))
+        (registry-group (and
+                         (bound-and-true-p gnus-registry-enabled)
+                         (car (gnus-registry-get-id-key
+                               (mail-header-id header) 'group))))
+        (registry-server
+         (and registry-group
+              (gnus-method-to-server
+               (gnus-find-method-for-group registry-group)))))
+    (when registry-server (cl-pushnew (list registry-server) server
+                                     :test 'equal))
+    (gnus-group-read-ephemeral-group
+     (concat "nnselect-" (message-unique-id))
+     (list 'nnselect "nnselect")
+     nil
+     (cons (current-buffer) gnus-current-window-configuration)
+                                       ;     nil
+     nil nil
+     (list
+      (cons 'nnselect-specs
+           (list
+            (cons 'nnselect-function 'nnir-run-query)
+            (cons 'nnselect-args
+                  (list (cons 'nnir-query-spec query)
+                        (cons 'nnir-group-spec server)))))
+      (cons 'nnselect-artlist nil)))
+    (gnus-summary-goto-subject (gnus-id-to-article (mail-header-id header)))))
+
+
+
+(defun nnselect-push-info (group)
+  "Copy mark-lists from GROUP to the originating groups."
+  (let ((select-unreads (numbers-by-group gnus-newsgroup-unreads))
+       (select-reads (numbers-by-group
+                      (gnus-uncompress-range
+                       (gnus-info-read (gnus-get-info group)))))
+       (select-unseen (numbers-by-group gnus-newsgroup-unseen))
+       (gnus-newsgroup-active nil)
+       mark-list type-list)
+    (pcase-dolist (`(,mark . ,type) gnus-article-mark-lists)
+      (when (setq type-list
+                 (symbol-value (intern (format "gnus-newsgroup-%s" mark))))
+       (push (cons type
+                   (numbers-by-group
+                    (gnus-uncompress-range type-list))) mark-list)))
+    (pcase-dolist (`(,artgroup . ,artlist)
+                  (numbers-by-group gnus-newsgroup-articles))
+      (let* ((group-info (gnus-get-info artgroup))
+            (old-unread (gnus-list-of-unread-articles artgroup))
+            newmarked)
+       (when group-info
+         (pcase-dolist (`(,_mark . ,type) gnus-article-mark-lists)
+           (let ((select-type
+                  (sort
+                   (cdr (assoc artgroup  (alist-get type mark-list)))
+                   '<))  list)
+             (setq list
+                   (gnus-uncompress-range
+                    (gnus-add-to-range
+                     (gnus-remove-from-range
+                      (alist-get type (gnus-info-marks group-info))
+                      artlist)
+                     select-type)))
+
+             (when list
+               ;; Get rid of the entries of the articles that have the
+               ;; default score.
+               (when (and (eq type 'score)
+                          gnus-save-score
+                          list)
+                 (let* ((arts list)
+                        (prev (cons nil list))
+                        (all prev))
+                   (while arts
+                     (if (or (not (consp (car arts)))
+                             (= (cdar arts) gnus-summary-default-score))
+                         (setcdr prev (cdr arts))
+                       (setq prev arts))
+                     (setq arts (cdr arts)))
+                   (setq list (cdr all)))))
+
+             (when  (or (eq (gnus-article-mark-to-type type) 'list)
+                        (eq (gnus-article-mark-to-type type) 'range))
+               (setq list
+                     (gnus-compress-sequence  (sort list '<) t)))
+
+             ;; When exiting the group, everything that's previously been
+             ;; unseen is now seen.
+             (when (eq  type 'seen)
+               (setq list (gnus-range-add
+                           list (cdr (assoc artgroup select-unseen)))))
+
+             (when (or list (eq  type 'unexist))
+               (push (cons  type list) newmarked))))
+
+         (gnus-atomic-progn
+           ;; Enter these new marks into the info of the group.
+           (if (nthcdr 3 group-info)
+               (setcar (nthcdr 3 group-info) newmarked)
+             ;; Add the marks lists to the end of the info.
+             (when newmarked
+               (setcdr (nthcdr 2 group-info) (list newmarked))))
+
+           ;; Cut off the end of the info if there's nothing else there.
+           (let ((i 5))
+             (while (and (> i 2)
+                         (not (nth i group-info)))
+               (when (nthcdr (cl-decf i) group-info)
+                 (setcdr (nthcdr i group-info) nil))))
+
+           ;; update read and unread
+           (gnus-update-read-articles
+            artgroup
+            (gnus-uncompress-range
+             (gnus-add-to-range
+              (gnus-remove-from-range
+               old-unread
+               (cdr (assoc artgroup select-reads)))
+              (sort (cdr (assoc artgroup select-unreads)) '<))))
+           (gnus-get-unread-articles-in-group
+            group-info (gnus-active artgroup) t)
+           (gnus-group-update-group artgroup t t)))))))
+
+
+(declare-function gnus-registry-get-id-key "gnus-registry" (id key))
+
+(defun gnus-summary-make-search-group (nnir-extra-parms)
+  "Search a group from the summary buffer.
+Pass NNIR-EXTRA-PARMS on to the search engine."
+  (interactive "P")
+  (gnus-warp-to-article)
+  (let ((spec
+        (list
+         (cons 'nnir-group-spec
+               (list (list
+                      (gnus-group-server gnus-newsgroup-name)
+                      gnus-newsgroup-name))))))
+    (gnus-group-make-search-group nnir-extra-parms spec)))
+
+
+;; The end.
+(provide 'nnselect)
+
+;;; nnselect.el ends here
diff --git a/lisp/gnus/nnspool.el b/lisp/gnus/nnspool.el
index 33b68fa..0b6bba5 100644
--- a/lisp/gnus/nnspool.el
+++ b/lisp/gnus/nnspool.el
@@ -422,7 +422,7 @@ there.")
              (nnspool-article-pathname nnspool-current-group article))
         (nnheader-insert-article-line article)
         (goto-char (point-min))
-        (let ((headers (nnheader-parse-head)))
+        (let ((headers (nnheader-parse-head nil t)))
           (set-buffer cur)
           (goto-char (point-max))
           (nnheader-insert-nov headers)))



reply via email to

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