aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/misc/gnus.texi259
-rw-r--r--etc/NEWS19
-rw-r--r--lisp/gnus/gnus-agent.el2
-rw-r--r--lisp/gnus/gnus-cache.el2
-rw-r--r--lisp/gnus/gnus-cloud.el10
-rw-r--r--lisp/gnus/gnus-group.el54
-rw-r--r--lisp/gnus/gnus-msg.el120
-rw-r--r--lisp/gnus/gnus-registry.el13
-rw-r--r--lisp/gnus/gnus-srvr.el5
-rw-r--r--lisp/gnus/gnus-start.el2
-rw-r--r--lisp/gnus/gnus-sum.el295
-rw-r--r--lisp/gnus/gnus.el7
-rw-r--r--lisp/gnus/nndiary.el2
-rw-r--r--lisp/gnus/nnfolder.el2
-rw-r--r--lisp/gnus/nnheader.el344
-rw-r--r--lisp/gnus/nnimap.el10
-rw-r--r--lisp/gnus/nnir.el857
-rw-r--r--lisp/gnus/nnmaildir.el2
-rw-r--r--lisp/gnus/nnml.el2
-rw-r--r--lisp/gnus/nnselect.el864
-rw-r--r--lisp/gnus/nnspool.el2
21 files changed, 1651 insertions, 1222 deletions
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi
index 0bdc2fa297d..593f113ac14 100644
--- a/doc/misc/gnus.texi
+++ b/doc/misc/gnus.texi
@@ -641,7 +641,7 @@ Select Methods
641* Getting Mail:: Reading your personal mail with Gnus. 641* Getting Mail:: Reading your personal mail with Gnus.
642* Browsing the Web:: Getting messages from a plethora of Web sources. 642* Browsing the Web:: Getting messages from a plethora of Web sources.
643* Other Sources:: Reading directories, files. 643* Other Sources:: Reading directories, files.
644* Combined Groups:: Combining groups into one group. 644* Virtual Groups:: Combining articles from multiple sources.
645* Email Based Diary:: Using mails to manage diary events in Gnus. 645* Email Based Diary:: Using mails to manage diary events in Gnus.
646* Gnus Unplugged:: Reading news and mail offline. 646* Gnus Unplugged:: Reading news and mail offline.
647 647
@@ -716,9 +716,10 @@ Document Groups
716 716
717* Document Server Internals:: How to add your own document types. 717* Document Server Internals:: How to add your own document types.
718 718
719Combined Groups 719Virtual Groups
720 720
721* Virtual Groups:: Combining articles from many groups. 721* Selection Groups:: Articles selected from many places.
722* Combined Groups:: Combining multiple groups.
722 723
723Email Based Diary 724Email Based Diary
724 725
@@ -10407,12 +10408,20 @@ article (@code{gnus-summary-refer-references}).
10407@findex gnus-summary-refer-thread 10408@findex gnus-summary-refer-thread
10408@kindex A T @r{(Summary)} 10409@kindex A T @r{(Summary)}
10409Display the full thread where the current article appears 10410Display the full thread where the current article appears
10410(@code{gnus-summary-refer-thread}). This command has to fetch all the 10411(@code{gnus-summary-refer-thread}). By default this command looks for
10411headers in the current group to work, so it usually takes a while. If 10412articles only in the current group. Some backends (currently only
10412you do it often, you may consider setting @code{gnus-fetch-old-headers} 10413'nnimap) know how to find articles in the thread directly. In other
10413to @code{invisible} (@pxref{Filling In Threads}). This won't have any 10414cases each header in the current group must be fetched and examined,
10414visible effects normally, but it'll make this command work a whole lot 10415so it usually takes a while. If you do it often, you may consider
10415faster. Of course, it'll make group entry somewhat slow. 10416setting @code{gnus-fetch-old-headers} to @code{invisible}
10417(@pxref{Filling In Threads}). This won't have any visible effects
10418normally, but it'll make this command work a whole lot faster. Of
10419course, it'll make group entry somewhat slow.
10420
10421@vindex gnus-refer-thread-use-search
10422If @code{gnus-refer-thread-use-search} is non-nil then those backends
10423that know how to find threads directly will search not just in the
10424current group but all groups on the same server.
10416 10425
10417@vindex gnus-refer-thread-limit 10426@vindex gnus-refer-thread-limit
10418The @code{gnus-refer-thread-limit} variable says how many old (i.e., 10427The @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
10421the available headers will be fetched. This variable can be overridden 10430the available headers will be fetched. This variable can be overridden
10422by giving the @kbd{A T} command a numerical prefix. 10431by giving the @kbd{A T} command a numerical prefix.
10423 10432
10433@vindex gnus-refer-thread-limit-to-thread
10434In most cases @code{gnus-refer-thread} adds any articles it finds to
10435the current summary buffer. (When @code{gnus-refer-thread-use-search}
10436is true and the initial referral starts from a summary buffer for a
10437non-virtual group this may not be possible. In this case a new summary
10438buffer is created holding a virtual group with the result of the thread
10439search). If @code{gnus-refer-thread-limit-to-thread} is non-nil then
10440the summary buffer will be limited to articles in the thread.
10441
10424@item M-^ (Summary) 10442@item M-^ (Summary)
10425@findex gnus-summary-refer-article 10443@findex gnus-summary-refer-article
10426@kindex M-^ @r{(Summary)} 10444@kindex M-^ @r{(Summary)}
@@ -13262,7 +13280,7 @@ The different methods all have their peculiarities, of course.
13262* Getting Mail:: Reading your personal mail with Gnus. 13280* Getting Mail:: Reading your personal mail with Gnus.
13263* Browsing the Web:: Getting messages from a plethora of Web sources. 13281* Browsing the Web:: Getting messages from a plethora of Web sources.
13264* Other Sources:: Reading directories, files. 13282* Other Sources:: Reading directories, files.
13265* Combined Groups:: Combining groups into one group. 13283* Virtual Groups:: Combining articles and groups together.
13266* Email Based Diary:: Using mails to manage diary events in Gnus. 13284* Email Based Diary:: Using mails to manage diary events in Gnus.
13267* Gnus Unplugged:: Reading news and mail offline. 13285* Gnus Unplugged:: Reading news and mail offline.
13268@end menu 13286@end menu
@@ -17834,19 +17852,133 @@ methods, but want to only use secondary ones:
17834@end lisp 17852@end lisp
17835 17853
17836 17854
17837@node Combined Groups 17855@node Virtual Groups
17838@section Combined Groups 17856@section Virtual Groups
17839 17857
17840Gnus allows combining a mixture of all the other group types into bigger 17858Gnus allows combining articles from many sources, and combinations of
17841groups. 17859whole groups together into virtual groups.
17842 17860
17843@menu 17861@menu
17844* Virtual Groups:: Combining articles from many groups. 17862* Selection Groups:: Combining articles from many groups.
17863* Combined Groups:: Combining multiple groups.
17845@end menu 17864@end menu
17846 17865
17847 17866
17848@node Virtual Groups 17867@node Selection Groups
17849@subsection Virtual Groups 17868@subsection Select Groups
17869@cindex nnselect
17870@cindex select groups
17871@cindex selecting articles
17872
17873
17874Gnus provides the @dfn{nnselect} method for creating virtual groups
17875composed of collections of messages, even when these messages come
17876from groups that span multiple servers and backends. For the most part
17877these virtual groups behave like any other group: messages may be
17878threaded, marked, moved, deleted, copied, etc.; groups may be
17879ephemeral or persistent; groups may be created via
17880@code{gnus-group-make-group} or browsed as foreign via
17881@code{gnus-group-browse-foreign-server}.
17882
17883The key to using an nnselect group is specifying the messages to
17884include. Each nnselect group has a group parameter
17885@code{nnselect-specs} which is an alist with two elements: a function
17886@code{nnselect-function}; and arguments @code{nnselect-args} to be
17887passed to the function, if any.
17888
17889The function @code{nnselect-function} must return a vector. Each
17890element of this vector is in turn a 3-element vector corresponding to
17891one message. The 3 elements are: the fully-qualified group name; the
17892message number; and a "score" that can be used for additional
17893sorting. The values for the score are arbitrary, and are not used
17894directly by the nnselect method---they may, for example, all be set to
17895100.
17896
17897Here is an example:
17898
17899@lisp
17900 (nnselect-specs
17901 (nnselect-function . identity)
17902 (nnselect-args .
17903 [["nnimap+work:mail" 595 100]
17904 ["nnimap+home:sent" 223 100]
17905 ["nntp+news.gmane.org:gmane.emacs.gnus.general" 23666 100]]))
17906@end lisp
17907
17908The function is the identity and the argument is just the list of
17909messages to include in the virtual group.
17910
17911Or we may wish to create a group from the results of a search query:
17912
17913@lisp
17914 (nnselect-specs
17915 (nnselect-function . nnir-run-query)
17916 (nnselect-args
17917 (nnir-query-spec
17918 (query . "FLAGGED")
17919 (criteria . ""))
17920 (nnir-group-spec
17921 ("nnimap:home")
17922 ("nnimap:work"))))
17923@end lisp
17924
17925This creates a group including all flagged messages from all groups on
17926two imap servers, "home" and "work".
17927
17928And one last example. Here is a function that runs a search query to
17929find all message that have been received recently from certain groups:
17930
17931@lisp
17932(defun my-recent-email (args)
17933 (let ((query-spec
17934 (list
17935 (cons 'query
17936 (format-time-string "SENTSINCE %d-%b-%Y"
17937 (time-subtract (current-time)
17938 (days-to-time (car args)))))
17939 (cons 'criteria "")))
17940 (group-spec (cadr args)))
17941 (nnir-run-query (cons 'nnir-specs
17942 (list (cons 'nnir-query-spec query-spec)
17943 (cons 'nnir-group-spec group-spec))))))
17944@end lisp
17945
17946Then an nnselect-specs
17947
17948@lisp
17949 (nnselect-specs
17950 (nnselect-function . my-recent-email)
17951 (nnselect-args . (7 (("nnimap:home") ("nnimap:work")))))
17952@end lisp
17953
17954will provide a group composed of all messages on the home and work
17955servers received in the last 7 days.
17956
17957Refreshing the selection of an nnselect group by running the
17958@code{nnselect-function} may take a long time to
17959complete. Consequently nnselect groups are not refreshed by default
17960when @code{gnus-group-get-new-news} is invoked. In those cases where
17961running the function is not too time-consuming, a non-nil group
17962parameter of @code{nnselect-rescan} will allow automatic refreshing. A
17963refresh can always be invoked manually through
17964@code{gnus-group-get-new-news-this-group}.
17965
17966The nnir interface (@pxref{nnir}) includes engines for searching a
17967variety of backends. While the details of each search engine vary, the
17968result of an nnir search is always a vector of the sort used by the
17969nnselect method, and the results of nnir queries are usually viewed
17970using an nnselect group. Indeed the standard search function
17971@code{gnus-group-read-ephemeral-search-group} just creates an
17972ephemeral nnselect group with the appropriate nnir query as the
17973@code{nnselect-specs}. nnir originally included both the search
17974engines and the glue to connect search results to gnus. Over time this
17975glue evolved into the nnselect method. The two had
17976a mostly amicable parting so that nnselect could pursue its dream of
17977becoming a fully functioning backend, but occasional conflicts may
17978still linger.
17979
17980@node Combined Groups
17981@subsection Combined Groups
17850@cindex nnvirtual 17982@cindex nnvirtual
17851@cindex virtual groups 17983@cindex virtual groups
17852@cindex merging groups 17984@cindex merging groups
@@ -21238,14 +21370,26 @@ four days, Gnus will decay the scores four times, for instance.
21238@chapter Searching 21370@chapter Searching
21239@cindex searching 21371@cindex searching
21240 21372
21241FIXME: Add a brief overview of Gnus search capabilities. A brief 21373FIXME: A brief comparison of nnir, nnmairix, contrib/gnus-namazu would
21242comparison of nnir, nnmairix, contrib/gnus-namazu would be nice 21374be nice.
21243as well. 21375
21244 21376Gnus has various ways of finding articles that match certain criteria
21245This chapter describes tools for searching groups and servers for 21377(from a particular author, on a certain subject, etc). The simplest
21246articles matching a query and then retrieving those articles. Gnus 21378method is to enter a group and then either "limit" the summary buffer
21247provides a simpler mechanism for searching through articles in a summary buffer 21379to the desired articles using the limiting commands (@xref{Limiting}),
21248to find those matching a pattern. @xref{Searching for Articles}. 21380or searching through messages in the summary buffer (@xref{Searching
21381for Articles}).
21382
21383Limiting commands and summary buffer searching work on subsets of the
21384articles already fetched from the servers, and these commands won’t
21385query the server for additional articles. While simple, these methods
21386are therefore inadequate if the desired articles span multiple groups,
21387or if the group is so large that fetching all articles is
21388impractical. Many backends (such as imap, notmuch, namazu, etc.)
21389provide their own facilities to search for articles directly on the
21390server and gnus can take advantage of these methods. This chapter
21391describes tools for searching groups and servers for articles matching
21392a query.
21249 21393
21250@menu 21394@menu
21251* nnir:: Searching with various engines. 21395* nnir:: Searching with various engines.
@@ -21275,7 +21419,7 @@ through mail and news repositories. Different backends (like
21275interface. 21419interface.
21276 21420
21277The @code{nnimap} search engine should work with no configuration. 21421The @code{nnimap} search engine should work with no configuration.
21278Other engines require a local index that needs to be created and 21422Other engines may require a local index that needs to be created and
21279maintained outside of Gnus. 21423maintained outside of Gnus.
21280 21424
21281 21425
@@ -21283,23 +21427,19 @@ maintained outside of Gnus.
21283@subsection Basic Usage 21427@subsection Basic Usage
21284 21428
21285In the group buffer typing @kbd{G G} will search the group on the 21429In the group buffer typing @kbd{G G} will search the group on the
21286current line by calling @code{gnus-group-make-nnir-group}. This prompts 21430current line by calling @code{gnus-group-make-search-group}. This prompts
21287for a query string, creates an ephemeral @code{nnir} group containing 21431for a query string, creates an ephemeral @code{nnselect} group containing
21288the articles that match this query, and takes you to a summary buffer 21432the articles that match this query, and takes you to a summary buffer
21289showing these articles. Articles may then be read, moved and deleted 21433showing these articles. Articles may then be read, moved and deleted
21290using the usual commands. 21434using the usual commands.
21291 21435
21292The @code{nnir} group made in this way is an @code{ephemeral} group, 21436The @code{nnselect} group made in this way is an @code{ephemeral}
21293and some changes are not permanent: aside from reading, moving, and 21437group, and will disappear upon exit from the group. However changes
21294deleting, you can't act on the original article. But there is an 21438made in the group are permanently reflected in the real groups from
21295alternative: you can @emph{warp} (i.e., jump) to the original group 21439which the articles are drawn. It is occasionally convenient to view
21296for the article on the current line with @kbd{A W}, aka 21440articles found through searching in their original group. You can
21297@code{gnus-warp-to-article}. Even better, the function 21441@emph{warp} (i.e., jump) to the original group for the article on the
21298@code{gnus-summary-refer-thread}, bound by default in summary buffers 21442current line with @kbd{A W}, aka @code{gnus-warp-to-article}.
21299to @kbd{A T}, will first warp to the original group before it works
21300its magic and includes all the articles in the thread. From here you
21301can read, move and delete articles, but also copy them, alter article
21302marks, whatever. Go nuts.
21303 21443
21304You say you want to search more than just the group on the current line? 21444You say you want to search more than just the group on the current line?
21305No problem: just process-mark the groups you want to search. You want 21445No 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
21307will search all the groups under that heading. 21447will search all the groups under that heading.
21308 21448
21309Still not enough? OK, in the server buffer 21449Still not enough? OK, in the server buffer
21310@code{gnus-group-make-nnir-group} (now bound to @kbd{G}) will search all 21450@code{gnus-group-make-search-group} (now bound to @kbd{G}) will search
21311groups from the server on the current line. Too much? Want to ignore 21451all groups from the server on the current line. Too much? Want to
21312certain groups when searching, like spam groups? Just customize 21452ignore certain groups when searching, like spam groups? Just
21313@code{nnir-ignored-newsgroups}. 21453customize @code{nnir-ignored-newsgroups}.
21314 21454
21315One more thing: individual search engines may have special search 21455One more thing: individual search engines may have special search
21316features. You can access these special features by giving a prefix-arg 21456features. You can access these special features by giving a prefix-arg
21317to @code{gnus-group-make-nnir-group}. If you are searching multiple 21457to @code{gnus-group-make-search-group}. If you are searching multiple
21318groups with different search engines you will be prompted for the 21458groups with different search engines you will be prompted for the
21319special search features for each engine separately. 21459special search features for each engine separately.
21320 21460
@@ -21371,8 +21511,7 @@ variable is set to use the @code{imap} engine for all servers using the
21371your servers with an @code{nnimap} backend you could change this to 21511your servers with an @code{nnimap} backend you could change this to
21372 21512
21373@lisp 21513@lisp
21374'((nnimap . namazu) 21514'((nnimap . namazu))
21375 (nntp . gmane))
21376@end lisp 21515@end lisp
21377 21516
21378@node The imap Engine 21517@node The imap Engine
@@ -21575,7 +21714,7 @@ This engine is obsolete.
21575 21714
21576@item nnir-method-default-engines 21715@item nnir-method-default-engines
21577Alist of pairs of server backends and search engines. The default 21716Alist of pairs of server backends and search engines. The default
21578associations are 21717association is
21579@example 21718@example
21580(nnimap . imap) 21719(nnimap . imap)
21581@end example 21720@end example
@@ -21584,32 +21723,6 @@ associations are
21584A regexp to match newsgroups in the active file that should be skipped 21723A regexp to match newsgroups in the active file that should be skipped
21585when searching all groups on a server. 21724when searching all groups on a server.
21586 21725
21587@item nnir-summary-line-format
21588The format specification to be used for lines in an nnir summary buffer.
21589All the items from @code{gnus-summary-line-format} are available, along with
21590three items unique to nnir summary buffers:
21591
21592@example
21593%Z Search retrieval score value (integer)
21594%G Article original full group name (string)
21595%g Article original short group name (string)
21596@end example
21597
21598If @code{nil} (the default) this will use @code{gnus-summary-line-format}.
21599
21600@item nnir-retrieve-headers-override-function
21601If non-@code{nil}, a function that retrieves article headers rather than using
21602the gnus built-in function. This function takes an article list and
21603group as arguments and populates the @code{nntp-server-buffer} with the
21604retrieved headers. It should then return either 'nov or 'headers
21605indicating the retrieved header format. Failure to retrieve headers
21606should return @code{nil}.
21607
21608If this variable is @code{nil}, or if the provided function returns
21609@code{nil} for a search result, @code{gnus-retrieve-headers} will be
21610called instead."
21611
21612
21613@end table 21726@end table
21614 21727
21615 21728
diff --git a/etc/NEWS b/etc/NEWS
index e0ea8f53cc8..da3928d6e49 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -317,7 +317,24 @@ tags to be considered as well.
317** Gnus 317** Gnus
318 318
319+++ 319+++
320*** New user option 'gnus-dbus-close-on-sleep'. 320*** New backend 'nnselect'
321The newly added nnselect backend allows creating groups from an
322arbitrary list of articles that may come from multiple groups and
323servers. These groups generally behave like any other group: they may
324be ephemeral or persistent, and allow article marking, moving,
325deletion, etc. Nnselect groups may be created like any other group,
326but there is also a convenience function for the common case of
327obtaining the list of articles as a result of a search:
328'gnus-group-make-search-group' (G g) that will prompt for an nnir
329search query and create a dedicated group for that search. As part of
330this addition, the variable 'nnir-summary-line-format' has been
331removed; it's functionality is now available directly in the
332'gnus-summary-line-format' 'G' and 'g' specs. The variable
333'gnus-refer-thread-use-nnir' has been renamed
334'gnus-refer-thread-use-search'.
335
336+++
337*** New user option 'gnus-dbus-close-on-sleep'
321On systems with D-Bus support, it is now possible to register a signal 338On systems with D-Bus support, it is now possible to register a signal
322to close all Gnus servers before the system sleeps. 339to close all Gnus servers before the system sleeps.
323 340
diff --git a/lisp/gnus/gnus-agent.el b/lisp/gnus/gnus-agent.el
index 88873f47bd5..03e447e072a 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."
3934 (mm-with-unibyte-buffer 3934 (mm-with-unibyte-buffer
3935 (nnheader-insert-file-contents file) 3935 (nnheader-insert-file-contents file)
3936 (nnheader-remove-body) 3936 (nnheader-remove-body)
3937 (setq header (nnheader-parse-naked-head))) 3937 (setq header (nnheader-parse-head t)))
3938 (setf (mail-header-number header) (car downloaded)) 3938 (setf (mail-header-number header) (car downloaded))
3939 (if nov-arts 3939 (if nov-arts
3940 (let ((key (concat "^" (int-to-string (car nov-arts)) 3940 (let ((key (concat "^" (int-to-string (car nov-arts))
diff --git a/lisp/gnus/gnus-cache.el b/lisp/gnus/gnus-cache.el
index 02a8ea723d3..7ca3bf1ce1c 100644
--- a/lisp/gnus/gnus-cache.el
+++ b/lisp/gnus/gnus-cache.el
@@ -186,7 +186,7 @@ it's not cached."
186 (gnus-cache-update-file-total-fetched-for group file)) 186 (gnus-cache-update-file-total-fetched-for group file))
187 (setq lines-chars (nnheader-get-lines-and-char)) 187 (setq lines-chars (nnheader-get-lines-and-char))
188 (nnheader-remove-body) 188 (nnheader-remove-body)
189 (setq headers (nnheader-parse-naked-head)) 189 (setq headers (nnheader-parse-head t))
190 (setf (mail-header-number headers) number) 190 (setf (mail-header-number headers) number)
191 (setf (mail-header-lines headers) (car lines-chars)) 191 (setf (mail-header-lines headers) (car lines-chars))
192 (setf (mail-header-chars headers) (cadr lines-chars)) 192 (setf (mail-header-chars headers) (cadr lines-chars))
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el
index 673a4d22988..e40b2eb418d 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."
391 (gnus-group-refresh-group group)) 391 (gnus-group-refresh-group group))
392 (gnus-error 2 "Failed to upload Gnus Cloud data to %s" group))))) 392 (gnus-error 2 "Failed to upload Gnus Cloud data to %s" group)))))
393 393
394(defvar gnus-alter-header-function)
395
394(defun gnus-cloud-add-timestamps (elems) 396(defun gnus-cloud-add-timestamps (elems)
395 (dolist (elem elems) 397 (dolist (elem elems)
396 (let* ((file-name (plist-get elem :file-name)) 398 (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."
409 (when (gnus-retrieve-headers (gnus-uncompress-range active) group) 411 (when (gnus-retrieve-headers (gnus-uncompress-range active) group)
410 (with-current-buffer nntp-server-buffer 412 (with-current-buffer nntp-server-buffer
411 (goto-char (point-min)) 413 (goto-char (point-min))
412 (while (and (not (eobp)) 414 (while (setq head (nnheader-parse-head))
413 (setq head (nnheader-parse-head))) 415 (when gnus-alter-header-function
414 (push head headers)))) 416 (funcall gnus-alter-header-function head))
417 (push head headers))
418 ))
415 (sort (nreverse headers) 419 (sort (nreverse headers)
416 (lambda (h1 h2) 420 (lambda (h1 h2)
417 (> (gnus-cloud-chunk-sequence (mail-header-subject h1)) 421 (> (gnus-cloud-chunk-sequence (mail-header-subject h1))
diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el
index 2cbbe624602..ad6e0e30bca 100644
--- a/lisp/gnus/gnus-group.el
+++ b/lisp/gnus/gnus-group.el
@@ -49,8 +49,6 @@
49(autoload 'gnus-agent-total-fetched-for "gnus-agent") 49(autoload 'gnus-agent-total-fetched-for "gnus-agent")
50(autoload 'gnus-cache-total-fetched-for "gnus-cache") 50(autoload 'gnus-cache-total-fetched-for "gnus-cache")
51 51
52(autoload 'gnus-group-make-nnir-group "nnir")
53
54(autoload 'gnus-cloud-upload-all-data "gnus-cloud") 52(autoload 'gnus-cloud-upload-all-data "gnus-cloud")
55(autoload 'gnus-cloud-download-all-data "gnus-cloud") 53(autoload 'gnus-cloud-download-all-data "gnus-cloud")
56 54
@@ -663,7 +661,8 @@ simple manner."
663 "D" gnus-group-enter-directory 661 "D" gnus-group-enter-directory
664 "f" gnus-group-make-doc-group 662 "f" gnus-group-make-doc-group
665 "w" gnus-group-make-web-group 663 "w" gnus-group-make-web-group
666 "G" gnus-group-make-nnir-group 664 "G" gnus-group-read-ephemeral-search-group
665 "g" gnus-group-make-search-group
667 "M" gnus-group-read-ephemeral-group 666 "M" gnus-group-read-ephemeral-group
668 "r" gnus-group-rename-group 667 "r" gnus-group-rename-group
669 "R" gnus-group-make-rss-group 668 "R" gnus-group-make-rss-group
@@ -909,7 +908,8 @@ simple manner."
909 ["Add the help group" gnus-group-make-help-group t] 908 ["Add the help group" gnus-group-make-help-group t]
910 ["Make a doc group..." gnus-group-make-doc-group t] 909 ["Make a doc group..." gnus-group-make-doc-group t]
911 ["Make a web group..." gnus-group-make-web-group t] 910 ["Make a web group..." gnus-group-make-web-group t]
912 ["Make a search group..." gnus-group-make-nnir-group t] 911 ["Read a search group..." gnus-group-read-ephemeral-search-group t]
912 ["Make a search group..." gnus-group-make-search-group t]
913 ["Make a virtual group..." gnus-group-make-empty-virtual t] 913 ["Make a virtual group..." gnus-group-make-empty-virtual t]
914 ["Add a group to a virtual..." gnus-group-add-to-virtual t] 914 ["Add a group to a virtual..." gnus-group-add-to-virtual t]
915 ["Make an ephemeral group..." gnus-group-read-ephemeral-group t] 915 ["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."
3166 (gnus-group-real-name group) 3166 (gnus-group-real-name group)
3167 (list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir))))) 3167 (list 'nndir (gnus-group-real-name group) (list 'nndir-directory dir)))))
3168 3168
3169
3170(autoload 'nnir-make-specs "nnir")
3171(autoload 'gnus-group-topic-name "gnus-topic")
3172
3173;; Temporary to make group creation easier
3174(defun gnus-group-make-search-group (nnir-extra-parms &optional specs)
3175 (interactive "P")
3176 (let ((name (gnus-read-group "Group name: ")))
3177 (with-current-buffer gnus-group-buffer
3178 (gnus-group-make-group
3179 name
3180 (list 'nnselect "nnselect")
3181 nil
3182 (list
3183 (cons 'nnselect-specs
3184 (list
3185 (cons 'nnselect-function 'nnir-run-query)
3186 (cons 'nnselect-args
3187 (nnir-make-specs nnir-extra-parms specs)))))))))
3188
3189(defun gnus-group-read-ephemeral-search-group (nnir-extra-parms &optional specs)
3190 "Create an nnselect group based on a search. Prompt for a
3191search query and determine the groups to search as follows: if
3192called from the *Server* buffer search all groups belonging to
3193the server on the current line; if called from the *Group* buffer
3194search any marked groups, or the group on the current line, or
3195all the groups under the current topic. Calling with a prefix-arg
3196prompts for additional search-engine specific constraints. A
3197non-nil `specs' arg must be an alist with `nnir-query-spec' and
3198`nnir-group-spec' keys, and skips all prompting."
3199 (interactive "P")
3200 (gnus-group-read-ephemeral-group
3201 (concat "nnselect-" (message-unique-id))
3202 (list 'nnselect "nnselect")
3203 nil
3204 (cons (current-buffer) gnus-current-window-configuration)
3205 ; nil
3206 nil nil
3207 (list
3208 (cons 'nnselect-specs
3209 (list
3210 (cons 'nnselect-function 'nnir-run-query)
3211 (cons 'nnselect-args
3212 (nnir-make-specs nnir-extra-parms specs))))
3213 (cons 'nnselect-artlist nil))))
3214
3169(defun gnus-group-add-to-virtual (n vgroup) 3215(defun gnus-group-add-to-virtual (n vgroup)
3170 "Add the current group to a virtual group." 3216 "Add the current group to a virtual group."
3171 (interactive 3217 (interactive
diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el
index e770abc2cdf..7bc7fb5be41 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."
393 (gnus-inews-make-draft-meta-information 393 (gnus-inews-make-draft-meta-information
394 ,gnus-newsgroup-name ',articles))) 394 ,gnus-newsgroup-name ',articles)))
395 395
396(autoload 'nnir-article-number "nnir" nil nil 'macro) 396(autoload 'nnselect-article-number "nnselect" nil nil 'macro)
397(autoload 'nnir-article-group "nnir" nil nil 'macro) 397(autoload 'nnselect-article-group "nnselect" nil nil 'macro)
398(autoload 'gnus-nnir-group-p "nnir") 398(autoload 'gnus-nnselect-group-p "nnselect")
399
400 399
401(defvar gnus-article-reply nil) 400(defvar gnus-article-reply nil)
402(defmacro gnus-setup-message (config &rest forms) 401(defmacro gnus-setup-message (config &rest forms)
@@ -404,22 +403,24 @@ only affect the Gcc copy, but not the original message."
404 (winconf-name (make-symbol "gnus-setup-message-winconf-name")) 403 (winconf-name (make-symbol "gnus-setup-message-winconf-name"))
405 (buffer (make-symbol "gnus-setup-message-buffer")) 404 (buffer (make-symbol "gnus-setup-message-buffer"))
406 (article (make-symbol "gnus-setup-message-article")) 405 (article (make-symbol "gnus-setup-message-article"))
406 (oarticle (make-symbol "gnus-setup-message-oarticle"))
407 (yanked (make-symbol "gnus-setup-yanked-articles")) 407 (yanked (make-symbol "gnus-setup-yanked-articles"))
408 (group (make-symbol "gnus-setup-message-group"))) 408 (group (make-symbol "gnus-setup-message-group")))
409 `(let ((,winconf (current-window-configuration)) 409 `(let ((,winconf (current-window-configuration))
410 (,winconf-name gnus-current-window-configuration) 410 (,winconf-name gnus-current-window-configuration)
411 (,buffer (buffer-name (current-buffer))) 411 (,buffer (buffer-name (current-buffer)))
412 (,article (if (and (gnus-nnir-group-p gnus-newsgroup-name) 412 (,article (when gnus-article-reply
413 gnus-article-reply) 413 (or (nnselect-article-number
414 (nnir-article-number (or (car-safe gnus-article-reply) 414 (or (car-safe gnus-article-reply)
415 gnus-article-reply)) 415 gnus-article-reply))
416 gnus-article-reply)) 416 gnus-article-reply)))
417 (,oarticle gnus-article-reply)
417 (,yanked gnus-article-yanked-articles) 418 (,yanked gnus-article-yanked-articles)
418 (,group (if (and (gnus-nnir-group-p gnus-newsgroup-name) 419 (,group (when gnus-article-reply
419 gnus-article-reply) 420 (or (nnselect-article-group
420 (nnir-article-group (or (car-safe gnus-article-reply) 421 (or (car-safe gnus-article-reply)
421 gnus-article-reply)) 422 gnus-article-reply))
422 gnus-newsgroup-name)) 423 gnus-newsgroup-name)))
423 (message-header-setup-hook 424 (message-header-setup-hook
424 (copy-sequence message-header-setup-hook)) 425 (copy-sequence message-header-setup-hook))
425 (mbl mml-buffer-list) 426 (mbl mml-buffer-list)
@@ -460,24 +461,23 @@ only affect the Gcc copy, but not the original message."
460 (unwind-protect 461 (unwind-protect
461 (progn 462 (progn
462 ,@forms) 463 ,@forms)
463 (gnus-inews-add-send-actions ,winconf ,buffer ,article ,config 464 (gnus-inews-add-send-actions ,winconf ,buffer ,oarticle ,config
464 ,yanked ,winconf-name) 465 ,yanked ,winconf-name)
465 (setq gnus-message-buffer (current-buffer)) 466 (setq gnus-message-buffer (current-buffer))
466 (set (make-local-variable 'gnus-message-group-art) 467 (set (make-local-variable 'gnus-message-group-art)
467 (cons ,group ,article)) 468 (cons ,group ,article))
468 (set (make-local-variable 'gnus-newsgroup-name) ,group) 469 ;; Enable highlighting of different citation levels
469 ;; Enable highlighting of different citation levels 470 (when gnus-message-highlight-citation
470 (when gnus-message-highlight-citation 471 (gnus-message-citation-mode 1))
471 (gnus-message-citation-mode 1)) 472 (gnus-run-hooks 'gnus-message-setup-hook)
472 (gnus-run-hooks 'gnus-message-setup-hook) 473 (if (eq major-mode 'message-mode)
473 (if (eq major-mode 'message-mode) 474 (let ((mbl1 mml-buffer-list))
474 (let ((mbl1 mml-buffer-list)) 475 (setq mml-buffer-list mbl) ;; Global value
475 (setq mml-buffer-list mbl) ;; Global value 476 (set (make-local-variable 'mml-buffer-list) mbl1);; Local value
476 (set (make-local-variable 'mml-buffer-list) mbl1);; Local value 477 (add-hook 'change-major-mode-hook 'mml-destroy-buffers nil t)
477 (add-hook 'change-major-mode-hook 'mml-destroy-buffers nil t) 478 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t))
478 (add-hook 'kill-buffer-hook 'mml-destroy-buffers t t)) 479 (mml-destroy-buffers)
479 (mml-destroy-buffers) 480 (setq mml-buffer-list mbl)))
480 (setq mml-buffer-list mbl)))
481 (message-hide-headers) 481 (message-hide-headers)
482 (gnus-add-buffer) 482 (gnus-add-buffer)
483 (gnus-configure-windows ,config t) 483 (gnus-configure-windows ,config t)
@@ -521,12 +521,10 @@ instead."
521 mail-buf) 521 mail-buf)
522 (unwind-protect 522 (unwind-protect
523 (progn 523 (progn
524 (setq gnus-newsgroup-name "") 524 (let ((gnus-newsgroup-name ""))
525 (gnus-setup-message 'message 525 (gnus-setup-message 'message
526 (message-mail to subject other-headers continue 526 (message-mail to subject other-headers continue
527 nil yank-action send-actions return-action))) 527 nil yank-action send-actions return-action)))))
528 (with-current-buffer buf
529 (setq gnus-newsgroup-name group-name)))
530 (when switch-action 528 (when switch-action
531 (setq mail-buf (current-buffer)) 529 (setq mail-buf (current-buffer))
532 (switch-to-buffer buf) 530 (switch-to-buffer buf)
@@ -617,18 +615,15 @@ If ARG is 1, prompt for a group name to find the posting style."
617 (buffer (current-buffer))) 615 (buffer (current-buffer)))
618 (unwind-protect 616 (unwind-protect
619 (progn 617 (progn
620 (setq gnus-newsgroup-name 618 (let ((gnus-newsgroup-name
621 (if arg 619 (if arg
622 (if (= 1 (prefix-numeric-value arg)) 620 (if (= 1 (prefix-numeric-value arg))
623 (gnus-group-completing-read 621 (gnus-group-completing-read
624 "Use posting style of group" 622 "Use posting style of group"
625 nil (gnus-read-active-file-p)) 623 nil (gnus-read-active-file-p))
626 (gnus-group-group-name)) 624 (gnus-group-group-name))
627 "")) 625 "")))
628 ;; #### see comment in gnus-setup-message -- drv 626 (gnus-setup-message 'message (message-mail)))))))
629 (gnus-setup-message 'message (message-mail)))
630 (with-current-buffer buffer
631 (setq gnus-newsgroup-name group)))))
632 627
633(defun gnus-group-news (&optional arg) 628(defun gnus-group-news (&optional arg)
634 "Start composing a news. 629 "Start composing a news.
@@ -647,19 +642,16 @@ network. The corresponding back end must have a `request-post' method."
647 (buffer (current-buffer))) 642 (buffer (current-buffer)))
648 (unwind-protect 643 (unwind-protect
649 (progn 644 (progn
650 (setq gnus-newsgroup-name 645 (let ((gnus-newsgroup-name
651 (if arg 646 (if arg
652 (if (= 1 (prefix-numeric-value arg)) 647 (if (= 1 (prefix-numeric-value arg))
653 (gnus-group-completing-read "Use group" 648 (gnus-group-completing-read "Use group"
654 nil 649 nil
655 (gnus-read-active-file-p)) 650 (gnus-read-active-file-p))
656 (gnus-group-group-name)) 651 (gnus-group-group-name))
657 "")) 652 "")))
658 ;; #### see comment in gnus-setup-message -- drv
659 (gnus-setup-message 'message 653 (gnus-setup-message 'message
660 (message-news (gnus-group-real-name gnus-newsgroup-name)))) 654 (message-news (gnus-group-real-name gnus-newsgroup-name))))))))
661 (with-current-buffer buffer
662 (setq gnus-newsgroup-name group)))))
663 655
664(defun gnus-group-post-news (&optional arg) 656(defun gnus-group-post-news (&optional arg)
665 "Start composing a message (a news by default). 657 "Start composing a message (a news by default).
@@ -694,18 +686,15 @@ posting style."
694 (buffer (current-buffer))) 686 (buffer (current-buffer)))
695 (unwind-protect 687 (unwind-protect
696 (progn 688 (progn
697 (setq gnus-newsgroup-name 689 (let ((gnus-newsgroup-name
698 (if arg 690 (if arg
699 (if (= 1 (prefix-numeric-value arg)) 691 (if (= 1 (prefix-numeric-value arg))
700 (gnus-group-completing-read "Use group" 692 (gnus-group-completing-read "Use group"
701 nil 693 nil
702 (gnus-read-active-file-p)) 694 (gnus-read-active-file-p))
703 "") 695 "")
704 gnus-newsgroup-name)) 696 gnus-newsgroup-name)))
705 ;; #### see comment in gnus-setup-message -- drv 697 (gnus-setup-message 'message (message-mail)))))))
706 (gnus-setup-message 'message (message-mail)))
707 (with-current-buffer buffer
708 (setq gnus-newsgroup-name group)))))
709 698
710(defun gnus-summary-news-other-window (&optional arg) 699(defun gnus-summary-news-other-window (&optional arg)
711 "Start composing a news in another window. 700 "Start composing a news in another window.
@@ -724,24 +713,21 @@ network. The corresponding back end must have a `request-post' method."
724 (buffer (current-buffer))) 713 (buffer (current-buffer)))
725 (unwind-protect 714 (unwind-protect
726 (progn 715 (progn
727 (setq gnus-newsgroup-name 716 (let ((gnus-newsgroup-name
728 (if arg 717 (if arg
729 (if (= 1 (prefix-numeric-value arg)) 718 (if (= 1 (prefix-numeric-value arg))
730 (gnus-group-completing-read "Use group" 719 (gnus-group-completing-read "Use group"
731 nil 720 nil
732 (gnus-read-active-file-p)) 721 (gnus-read-active-file-p))
733 "") 722 "")
734 gnus-newsgroup-name)) 723 gnus-newsgroup-name)))
735 ;; #### see comment in gnus-setup-message -- drv
736 (gnus-setup-message 'message 724 (gnus-setup-message 'message
737 (progn 725 (progn
738 (message-news (gnus-group-real-name gnus-newsgroup-name)) 726 (message-news (gnus-group-real-name gnus-newsgroup-name))
739 (set (make-local-variable 'gnus-discouraged-post-methods) 727 (set (make-local-variable 'gnus-discouraged-post-methods)
740 (remove 728 (remove
741 (car (gnus-find-method-for-group gnus-newsgroup-name)) 729 (car (gnus-find-method-for-group gnus-newsgroup-name))
742 gnus-discouraged-post-methods))))) 730 gnus-discouraged-post-methods)))))))))
743 (with-current-buffer buffer
744 (setq gnus-newsgroup-name group)))))
745 731
746(defun gnus-summary-post-news (&optional arg) 732(defun gnus-summary-post-news (&optional arg)
747 "Start composing a message. Post to the current group by default. 733 "Start composing a message. Post to the current group by default.
@@ -823,7 +809,7 @@ active, the entire article will be yanked."
823 (with-current-buffer gnus-article-copy 809 (with-current-buffer gnus-article-copy
824 (save-restriction 810 (save-restriction
825 (nnheader-narrow-to-headers) 811 (nnheader-narrow-to-headers)
826 (nnheader-parse-naked-head))))) 812 (nnheader-parse-head t)))))
827 (message-yank-original) 813 (message-yank-original)
828 (message-exchange-point-and-mark) 814 (message-exchange-point-and-mark)
829 (setq beg (or beg (mark t)))) 815 (setq beg (or beg (mark t))))
@@ -1993,10 +1979,10 @@ process-mark several articles, they will all be attached."
1993 (gnus-summary-iterate n 1979 (gnus-summary-iterate n
1994 (gnus-summary-select-article) 1980 (gnus-summary-select-article)
1995 (with-current-buffer destination 1981 (with-current-buffer destination
1996 ;; Attach at the end of the buffer. 1982 ;; Attach at the end of the buffer.
1997 (save-excursion 1983 (save-excursion
1998 (goto-char (point-max)) 1984 (goto-char (point-max))
1999 (message-forward-make-body-mime gnus-original-article-buffer)))) 1985 (message-forward-make-body-mime gnus-original-article-buffer))))
2000 (gnus-configure-windows 'message t))) 1986 (gnus-configure-windows 'message t)))
2001 1987
2002(provide 'gnus-msg) 1988(provide 'gnus-msg)
diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el
index 1ac1d05e033..65bcd0e8a36 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'."
427 (gnus-message 4 "Removed %d ignored entries from the Gnus registry" 427 (gnus-message 4 "Removed %d ignored entries from the Gnus registry"
428 (- old-size (registry-size db))))) 428 (- old-size (registry-size db)))))
429 429
430(declare-function gnus-nnselect-group-p "nnselect" (group))
431(declare-function nnselect-article-group "nnselect" (article))
430;; article move/copy/spool/delete actions 432;; article move/copy/spool/delete actions
431(defun gnus-registry-action (action data-header from &optional to method) 433(defun gnus-registry-action (action data-header from &optional to method)
432 (let* ((id (mail-header-id data-header)) 434 (let* ((id (mail-header-id data-header))
@@ -437,7 +439,10 @@ This is not required after changing `gnus-registry-cache-file'."
437 (or (cdr-safe (assq 'To extra)) ""))) 439 (or (cdr-safe (assq 'To extra)) "")))
438 (sender (nth 0 (gnus-registry-extract-addresses 440 (sender (nth 0 (gnus-registry-extract-addresses
439 (mail-header-from data-header)))) 441 (mail-header-from data-header))))
440 (from (gnus-group-guess-full-name-from-command-method from)) 442 (from (gnus-group-guess-full-name-from-command-method
443 (if (gnus-nnselect-group-p from)
444 (nnselect-article-group (mail-header-number data-header))
445 from)))
441 (to (if to (gnus-group-guess-full-name-from-command-method to) nil))) 446 (to (if to (gnus-group-guess-full-name-from-command-method to) nil)))
442 (gnus-message 7 "Gnus registry: article %s %s from %s to %s" 447 (gnus-message 7 "Gnus registry: article %s %s from %s to %s"
443 id (if method "respooling" "going") from to) 448 id (if method "respooling" "going") from to)
@@ -788,7 +793,7 @@ Consults `gnus-registry-unfollowed-groups' and
788Consults `gnus-registry-ignored-groups' and 793Consults `gnus-registry-ignored-groups' and
789`nnmail-split-fancy-with-parent-ignore-groups'." 794`nnmail-split-fancy-with-parent-ignore-groups'."
790 (and group 795 (and group
791 (or (gnus-grep-in-list 796 (or (gnus-virtual-group-p group) (gnus-grep-in-list
792 group 797 group
793 (delq nil (mapcar (lambda (g) 798 (delq nil (mapcar (lambda (g)
794 (cond 799 (cond
@@ -1218,7 +1223,7 @@ is `ask', ask the user; or if `gnus-registry-install' is non-nil, enable it."
1218 (gnus-registry-initialize))) 1223 (gnus-registry-initialize)))
1219 gnus-registry-enabled) 1224 gnus-registry-enabled)
1220 1225
1221;; largely based on nnir-warp-to-article 1226;; largely based on nnselect-warp-to-article
1222(defun gnus-try-warping-via-registry () 1227(defun gnus-try-warping-via-registry ()
1223 "Try to warp via the registry. 1228 "Try to warp via the registry.
1224This will be done via the current article's source group based on 1229This will be done via the current article's source group based on
@@ -1242,7 +1247,7 @@ data stored in the registry."
1242 (gnus-ephemeral-group-p group) ;; any ephemeral group 1247 (gnus-ephemeral-group-p group) ;; any ephemeral group
1243 (memq (car (gnus-find-method-for-group group)) 1248 (memq (car (gnus-find-method-for-group group))
1244 ;; Specific methods; this list may need to expand. 1249 ;; Specific methods; this list may need to expand.
1245 '(nnir))) 1250 '(nnselect)))
1246 1251
1247 ;; remember that we've seen this group already 1252 ;; remember that we've seen this group already
1248 (push group seen-groups) 1253 (push group seen-groups)
diff --git a/lisp/gnus/gnus-srvr.el b/lisp/gnus/gnus-srvr.el
index 095e05408d6..8cb80b2f520 100644
--- a/lisp/gnus/gnus-srvr.el
+++ b/lisp/gnus/gnus-srvr.el
@@ -34,7 +34,8 @@
34(require 'gnus-range) 34(require 'gnus-range)
35(require 'gnus-cloud) 35(require 'gnus-cloud)
36 36
37(autoload 'gnus-group-make-nnir-group "nnir") 37(autoload 'gnus-group-read-ephemeral-search-group "nnselect")
38;;(autoload 'gnus-group-make-permanent-search-group "nnselect")
38 39
39(defcustom gnus-server-exit-hook nil 40(defcustom gnus-server-exit-hook nil
40 "Hook run when exiting the server buffer." 41 "Hook run when exiting the server buffer."
@@ -176,7 +177,7 @@ If nil, a faster, but more primitive, buffer is used instead."
176 177
177 "g" gnus-server-regenerate-server 178 "g" gnus-server-regenerate-server
178 179
179 "G" gnus-group-make-nnir-group 180 "G" gnus-group-read-ephemeral-search-group
180 181
181 "z" gnus-server-compact-server 182 "z" gnus-server-compact-server
182 183
diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el
index fe600f107ce..e4f05de5f8e 100644
--- a/lisp/gnus/gnus-start.el
+++ b/lisp/gnus/gnus-start.el
@@ -1802,7 +1802,7 @@ backend check whether the group actually exists."
1802 ;; by one. 1802 ;; by one.
1803 (t 1803 (t
1804 (dolist (info infos) 1804 (dolist (info infos)
1805 (gnus-activate-group (gnus-info-group info) nil nil method t)))))) 1805 (gnus-activate-group (gnus-info-group info) t nil method t))))))
1806 1806
1807(defun gnus-make-hashtable-from-newsrc-alist () 1807(defun gnus-make-hashtable-from-newsrc-alist ()
1808 "Create a hash table from `gnus-newsrc-alist'. 1808 "Create a hash table from `gnus-newsrc-alist'.
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index c53f81fe026..8f37fc88284 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -85,8 +85,8 @@
85(autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t) 85(autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t)
86(autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t) 86(autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t)
87(autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t) 87(autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t)
88(autoload 'nnir-article-rsv "nnir" nil nil 'macro) 88(autoload 'nnselect-article-rsv "nnselect" nil nil)
89(autoload 'nnir-article-group "nnir" nil nil 'macro) 89(autoload 'nnselect-article-group "nnselect" nil nil)
90 90
91(defcustom gnus-kill-summary-on-exit t 91(defcustom gnus-kill-summary-on-exit t
92 "If non-nil, kill the summary buffer when you exit from it. 92 "If non-nil, kill the summary buffer when you exit from it.
@@ -144,9 +144,9 @@ If t, fetch all the available old headers."
144 :type '(choice number 144 :type '(choice number
145 (sexp :menu-tag "other" t))) 145 (sexp :menu-tag "other" t)))
146 146
147(defcustom gnus-refer-thread-use-nnir nil 147(defcustom gnus-refer-thread-use-search nil
148 "Use nnir to search an entire server when referring threads. 148 "Search an entire server when referring threads. A
149A nil value will only search for thread-related articles in the 149nil value will only search for thread-related articles in the
150current group." 150current group."
151 :version "24.1" 151 :version "24.1"
152 :group 'gnus-thread 152 :group 'gnus-thread
@@ -884,6 +884,7 @@ controls how articles are sorted."
884 (function-item gnus-article-sort-by-subject) 884 (function-item gnus-article-sort-by-subject)
885 (function-item gnus-article-sort-by-date) 885 (function-item gnus-article-sort-by-date)
886 (function-item gnus-article-sort-by-score) 886 (function-item gnus-article-sort-by-score)
887 (function-item gnus-article-sort-by-rsv)
887 (function-item gnus-article-sort-by-random) 888 (function-item gnus-article-sort-by-random)
888 (function :tag "other")) 889 (function :tag "other"))
889 (boolean :tag "Reverse order")))) 890 (boolean :tag "Reverse order"))))
@@ -927,6 +928,7 @@ subthreads, customize `gnus-subthread-sort-functions'."
927 (function-item gnus-thread-sort-by-subject) 928 (function-item gnus-thread-sort-by-subject)
928 (function-item gnus-thread-sort-by-date) 929 (function-item gnus-thread-sort-by-date)
929 (function-item gnus-thread-sort-by-score) 930 (function-item gnus-thread-sort-by-score)
931 (function-item gnus-thread-sort-by-rsv)
930 (function-item gnus-thread-sort-by-most-recent-number) 932 (function-item gnus-thread-sort-by-most-recent-number)
931 (function-item gnus-thread-sort-by-most-recent-date) 933 (function-item gnus-thread-sort-by-most-recent-date)
932 (function-item gnus-thread-sort-by-random) 934 (function-item gnus-thread-sort-by-random)
@@ -1433,16 +1435,13 @@ the normal Gnus MIME machinery."
1433 (?c (or (mail-header-chars gnus-tmp-header) 0) ?d) 1435 (?c (or (mail-header-chars gnus-tmp-header) 0) ?d)
1434 (?k (gnus-summary-line-message-size gnus-tmp-header) ?s) 1436 (?k (gnus-summary-line-message-size gnus-tmp-header) ?s)
1435 (?L gnus-tmp-lines ?s) 1437 (?L gnus-tmp-lines ?s)
1436 (?Z (or (nnir-article-rsv (mail-header-number gnus-tmp-header)) 1438 (?Z (or (nnselect-article-rsv (mail-header-number gnus-tmp-header))
1437 0) 1439 0) ?d)
1438 ?d) 1440 (?G (or (nnselect-article-group (mail-header-number gnus-tmp-header))
1439 (?G (or (nnir-article-group (mail-header-number gnus-tmp-header)) 1441 "") ?s)
1440 "")
1441 ?s)
1442 (?g (or (gnus-group-short-name 1442 (?g (or (gnus-group-short-name
1443 (nnir-article-group (mail-header-number gnus-tmp-header))) 1443 (nnselect-article-group (mail-header-number gnus-tmp-header)))
1444 "") 1444 "") ?s)
1445 ?s)
1446 (?O gnus-tmp-downloaded ?c) 1445 (?O gnus-tmp-downloaded ?c)
1447 (?I gnus-tmp-indentation ?s) 1446 (?I gnus-tmp-indentation ?s)
1448 (?T (if (= gnus-tmp-level 0) "" (make-string (frame-width) ? )) ?s) 1447 (?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.")
1619 1618
1620(defvar gnus-newsgroup-sparse nil) 1619(defvar gnus-newsgroup-sparse nil)
1621 1620
1621(defvar gnus-newsgroup-selection nil)
1622
1622(defvar gnus-current-article nil) 1623(defvar gnus-current-article nil)
1623(defvar gnus-article-current nil) 1624(defvar gnus-article-current nil)
1624(defvar gnus-current-headers nil) 1625(defvar gnus-current-headers nil)
@@ -1653,6 +1654,8 @@ This list will always be a subset of gnus-newsgroup-undownloaded.")
1653 gnus-newsgroup-undownloaded 1654 gnus-newsgroup-undownloaded
1654 gnus-newsgroup-unsendable 1655 gnus-newsgroup-unsendable
1655 1656
1657 gnus-newsgroup-selection
1658
1656 gnus-newsgroup-begin gnus-newsgroup-end 1659 gnus-newsgroup-begin gnus-newsgroup-end
1657 gnus-newsgroup-last-rmail gnus-newsgroup-last-mail 1660 gnus-newsgroup-last-rmail gnus-newsgroup-last-mail
1658 gnus-newsgroup-last-folder gnus-newsgroup-last-file 1661 gnus-newsgroup-last-folder gnus-newsgroup-last-file
@@ -4532,48 +4535,14 @@ Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
4532;; This function has to be called with point after the article number 4535;; This function has to be called with point after the article number
4533;; on the beginning of the line. 4536;; on the beginning of the line.
4534(defsubst gnus-nov-parse-line (number dependencies &optional force-new) 4537(defsubst gnus-nov-parse-line (number dependencies &optional force-new)
4535 (let ((eol (point-at-eol)) 4538 (let (header)
4536 header references in-reply-to)
4537
4538 ;; overview: [num subject from date id refs chars lines misc] 4539 ;; overview: [num subject from date id refs chars lines misc]
4539 (unwind-protect 4540 (unwind-protect
4540 (let (x) 4541 (narrow-to-region (point) (point-at-eol))
4541 (narrow-to-region (point) eol) 4542 (unless (eobp)
4542 (unless (eobp) 4543 (forward-char))
4543 (forward-char)) 4544 (setq header (nnheader-parse-nov number))
4544
4545 (setq header
4546 (make-full-mail-header
4547 number ; number
4548 (condition-case () ; subject
4549 (gnus-remove-odd-characters
4550 (funcall gnus-decode-encoded-word-function
4551 (setq x (nnheader-nov-field))))
4552 (error x))
4553 (condition-case () ; from
4554 (gnus-remove-odd-characters
4555 (funcall gnus-decode-encoded-address-function
4556 (setq x (nnheader-nov-field))))
4557 (error x))
4558 (nnheader-nov-field) ; date
4559 (nnheader-nov-read-message-id number) ; id
4560 (setq references (nnheader-nov-field)) ; refs
4561 (nnheader-nov-read-integer) ; chars
4562 (nnheader-nov-read-integer) ; lines
4563 (unless (eobp)
4564 (if (looking-at "Xref: ")
4565 (goto-char (match-end 0)))
4566 (nnheader-nov-field)) ; Xref
4567 (nnheader-nov-parse-extra)))) ; extra
4568
4569 (widen)) 4545 (widen))
4570
4571 (when (and (string= references "")
4572 (setq in-reply-to (mail-header-extra header))
4573 (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
4574 (setf (mail-header-references header)
4575 (gnus-extract-message-id-from-in-reply-to in-reply-to)))
4576
4577 (when gnus-alter-header-function 4546 (when gnus-alter-header-function
4578 (funcall gnus-alter-header-function header)) 4547 (funcall gnus-alter-header-function header))
4579 (gnus-dependencies-add-header header dependencies force-new))) 4548 (gnus-dependencies-add-header header dependencies force-new)))
@@ -5104,6 +5073,17 @@ using some other form will lead to serious barfage."
5104 (gnus-article-sort-by-date 5073 (gnus-article-sort-by-date
5105 (gnus-thread-header h1) (gnus-thread-header h2))) 5074 (gnus-thread-header h1) (gnus-thread-header h2)))
5106 5075
5076(defsubst gnus-article-sort-by-rsv (h1 h2)
5077 "Sort articles by rsv."
5078 (when gnus-newsgroup-selection
5079 (< (nnselect-article-rsv (mail-header-number h1))
5080 (nnselect-article-rsv (mail-header-number h2)))))
5081
5082(defun gnus-thread-sort-by-rsv (h1 h2)
5083 "Sort threads by root article rsv."
5084 (gnus-article-sort-by-rsv
5085 (gnus-thread-header h1) (gnus-thread-header h2)))
5086
5107(defsubst gnus-article-sort-by-score (h1 h2) 5087(defsubst gnus-article-sort-by-score (h1 h2)
5108 "Sort articles by root article score. 5088 "Sort articles by root article score.
5109Unscored articles will be counted as having a score of zero." 5089Unscored articles will be counted as having a score of zero."
@@ -5634,22 +5614,32 @@ or a straight list of headers."
5634 "Fetch headers of ARTICLES." 5614 "Fetch headers of ARTICLES."
5635 (gnus-message 7 "Fetching headers for %s..." gnus-newsgroup-name) 5615 (gnus-message 7 "Fetching headers for %s..." gnus-newsgroup-name)
5636 (prog1 5616 (prog1
5637 (if (eq 'nov 5617 (pcase (setq gnus-headers-retrieved-by
5638 (setq gnus-headers-retrieved-by 5618 (gnus-retrieve-headers
5639 (gnus-retrieve-headers 5619 articles gnus-newsgroup-name
5640 articles gnus-newsgroup-name 5620 (or limit
5641 (or limit 5621 ;; We might want to fetch old headers, but
5642 ;; We might want to fetch old headers, but 5622 ;; not if there is only 1 article.
5643 ;; not if there is only 1 article. 5623 (and (or (and
5644 (and (or (and 5624 (not (eq gnus-fetch-old-headers 'some))
5645 (not (eq gnus-fetch-old-headers 'some)) 5625 (not (numberp gnus-fetch-old-headers)))
5646 (not (numberp gnus-fetch-old-headers))) 5626 (> (length articles) 1))
5647 (> (length articles) 1)) 5627 gnus-fetch-old-headers))))
5648 gnus-fetch-old-headers))))) 5628 ('nov
5649 (gnus-get-newsgroup-headers-xover 5629 (gnus-get-newsgroup-headers-xover
5650 articles force-new dependencies gnus-newsgroup-name t) 5630 articles force-new dependencies gnus-newsgroup-name t))
5651 (gnus-get-newsgroup-headers dependencies force-new)) 5631 ('headers
5652 (gnus-message 7 "Fetching headers for %s...done" gnus-newsgroup-name))) 5632 (gnus-get-newsgroup-headers dependencies force-new))
5633 ((pred listp)
5634 (let ((dependencies
5635 (or dependencies
5636 (with-current-buffer gnus-summary-buffer
5637 gnus-newsgroup-dependencies))))
5638 (delq nil (mapcar #'(lambda (header)
5639 (gnus-dependencies-add-header
5640 header dependencies force-new))
5641 gnus-headers-retrieved-by)))))
5642 (gnus-message 7 "Fetching headers for %s...done" gnus-newsgroup-name)))
5653 5643
5654(defun gnus-select-newsgroup (group &optional read-all select-articles) 5644(defun gnus-select-newsgroup (group &optional read-all select-articles)
5655 "Select newsgroup GROUP. 5645 "Select newsgroup GROUP.
@@ -6405,12 +6395,11 @@ The resulting hash table is returned, or nil if no Xrefs were found."
6405 (gnus-group-update-group group t)))))) 6395 (gnus-group-update-group group t))))))
6406 6396
6407(defun gnus-get-newsgroup-headers (&optional dependencies force-new) 6397(defun gnus-get-newsgroup-headers (&optional dependencies force-new)
6408 (let ((cur nntp-server-buffer) 6398 (let ((dependencies
6409 (dependencies
6410 (or dependencies 6399 (or dependencies
6411 (with-current-buffer gnus-summary-buffer 6400 (with-current-buffer gnus-summary-buffer
6412 gnus-newsgroup-dependencies))) 6401 gnus-newsgroup-dependencies)))
6413 headers id end ref number 6402 headers
6414 (mail-parse-charset gnus-newsgroup-charset) 6403 (mail-parse-charset gnus-newsgroup-charset)
6415 (mail-parse-ignored-charsets 6404 (mail-parse-ignored-charsets
6416 (save-current-buffer (condition-case nil 6405 (save-current-buffer (condition-case nil
@@ -6418,146 +6407,15 @@ The resulting hash table is returned, or nil if no Xrefs were found."
6418 (error)) 6407 (error))
6419 gnus-newsgroup-ignored-charsets))) 6408 gnus-newsgroup-ignored-charsets)))
6420 (with-current-buffer nntp-server-buffer 6409 (with-current-buffer nntp-server-buffer
6421 ;; Translate all TAB characters into SPACE characters.
6422 (subst-char-in-region (point-min) (point-max) ?\t ? t)
6423 (subst-char-in-region (point-min) (point-max) ?\r ? t)
6424 (ietf-drums-unfold-fws)
6425 (gnus-run-hooks 'gnus-parse-headers-hook) 6410 (gnus-run-hooks 'gnus-parse-headers-hook)
6426 (let ((case-fold-search t) 6411 (let ((nnmail-extra-headers gnus-extra-headers)
6427 in-reply-to header p lines chars) 6412 header)
6428 (goto-char (point-min)) 6413 (goto-char (point-min))
6429 ;; Search to the beginning of the next header. Error messages 6414 (while (setq header (nnheader-parse-head))
6430 ;; do not begin with 2 or 3.
6431 (while (re-search-forward "^[23][0-9]+ " nil t)
6432 (setq id nil
6433 ref nil)
6434 ;; This implementation of this function, with nine
6435 ;; search-forwards instead of the one re-search-forward and
6436 ;; a case (which basically was the old function) is actually
6437 ;; about twice as fast, even though it looks messier. You
6438 ;; can't have everything, I guess. Speed and elegance
6439 ;; doesn't always go hand in hand.
6440 (setq
6441 header
6442 (make-full-mail-header
6443 ;; Number.
6444 (prog1
6445 (setq number (read cur))
6446 (end-of-line)
6447 (setq p (point))
6448 (narrow-to-region (point)
6449 (or (and (search-forward "\n.\n" nil t)
6450 (- (point) 2))
6451 (point))))
6452 ;; Subject.
6453 (progn
6454 (goto-char p)
6455 (if (search-forward "\nsubject:" nil t)
6456 (funcall gnus-decode-encoded-word-function
6457 (nnheader-header-value))
6458 "(none)"))
6459 ;; From.
6460 (progn
6461 (goto-char p)
6462 (if (search-forward "\nfrom:" nil t)
6463 (funcall gnus-decode-encoded-address-function
6464 (nnheader-header-value))
6465 "(nobody)"))
6466 ;; Date.
6467 (progn
6468 (goto-char p)
6469 (if (search-forward "\ndate:" nil t)
6470 (nnheader-header-value) ""))
6471 ;; Message-ID.
6472 (progn
6473 (goto-char p)
6474 (setq id (if (re-search-forward
6475 "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
6476 ;; We do it this way to make sure the Message-ID
6477 ;; is (somewhat) syntactically valid.
6478 (buffer-substring (match-beginning 1)
6479 (match-end 1))
6480 ;; If there was no message-id, we just fake one
6481 ;; to make subsequent routines simpler.
6482 (nnheader-generate-fake-message-id number))))
6483 ;; References.
6484 (progn
6485 (goto-char p)
6486 (if (search-forward "\nreferences:" nil t)
6487 (progn
6488 (setq end (point))
6489 (prog1
6490 (nnheader-header-value)
6491 (setq ref
6492 (buffer-substring
6493 (progn
6494 (end-of-line)
6495 (search-backward ">" end t)
6496 (1+ (point)))
6497 (progn
6498 (search-backward "<" end t)
6499 (point))))))
6500 ;; Get the references from the in-reply-to header if there
6501 ;; were no references and the in-reply-to header looks
6502 ;; promising.
6503 (if (and (search-forward "\nin-reply-to:" nil t)
6504 (setq in-reply-to (nnheader-header-value))
6505 (string-match "<[^>]+>" in-reply-to))
6506 (let (ref2)
6507 (setq ref (substring in-reply-to (match-beginning 0)
6508 (match-end 0)))
6509 (while (string-match "<[^>]+>" in-reply-to (match-end 0))
6510 (setq ref2 (substring in-reply-to (match-beginning 0)
6511 (match-end 0)))
6512 (when (> (length ref2) (length ref))
6513 (setq ref ref2)))
6514 ref)
6515 (setq ref nil))))
6516 ;; Chars.
6517 (progn
6518 (goto-char p)
6519 (if (search-forward "\nchars: " nil t)
6520 (if (numberp (setq chars (ignore-errors (read cur))))
6521 chars -1)
6522 -1))
6523 ;; Lines.
6524 (progn
6525 (goto-char p)
6526 (if (search-forward "\nlines: " nil t)
6527 (if (numberp (setq lines (ignore-errors (read cur))))
6528 lines -1)
6529 -1))
6530 ;; Xref.
6531 (progn
6532 (goto-char p)
6533 (and (search-forward "\nxref:" nil t)
6534 (nnheader-header-value)))
6535 ;; Extra.
6536 (when gnus-extra-headers
6537 (let ((extra gnus-extra-headers)
6538 out)
6539 (while extra
6540 (goto-char p)
6541 (when (search-forward
6542 (concat "\n" (symbol-name (car extra)) ":") nil t)
6543 (push (cons (car extra) (nnheader-header-value))
6544 out))
6545 (pop extra))
6546 out))))
6547 (when (equal id ref)
6548 (setq ref nil))
6549
6550 (when gnus-alter-header-function
6551 (funcall gnus-alter-header-function header)
6552 (setq id (mail-header-id header)
6553 ref (gnus-parent-id (mail-header-references header))))
6554
6555 (when (setq header 6415 (when (setq header
6556 (gnus-dependencies-add-header 6416 (gnus-dependencies-add-header
6557 header dependencies force-new)) 6417 header dependencies force-new))
6558 (push header headers)) 6418 (push header headers)))
6559 (goto-char (point-max))
6560 (widen))
6561 (nreverse headers))))) 6419 (nreverse headers)))))
6562 6420
6563;; Goes through the xover lines and returns a list of vectors 6421;; Goes through the xover lines and returns a list of vectors
@@ -8702,7 +8560,8 @@ SCORE."
8702When called interactively, ID is the Message-ID of the current 8560When called interactively, ID is the Message-ID of the current
8703article. If thread-only is non-nil limit the summary buffer to 8561article. If thread-only is non-nil limit the summary buffer to
8704these articles." 8562these articles."
8705 (interactive (list (mail-header-id (gnus-summary-article-header)))) 8563 (interactive (list (mail-header-id (gnus-summary-article-header))
8564 current-prefix-arg))
8706 (let ((articles (gnus-articles-in-thread 8565 (let ((articles (gnus-articles-in-thread
8707 (gnus-id-to-thread (gnus-root-id id)))) 8566 (gnus-id-to-thread (gnus-root-id id))))
8708 ;;we REALLY want the whole thread---this prevents cut-threads 8567 ;;we REALLY want the whole thread---this prevents cut-threads
@@ -9125,13 +8984,13 @@ Return the number of articles fetched."
9125 result)) 8984 result))
9126 8985
9127(defun gnus-summary-refer-thread (&optional limit) 8986(defun gnus-summary-refer-thread (&optional limit)
9128 "Fetch all articles in the current thread. For backends 8987 "Fetch all articles in the current thread. For backends that
9129that know how to search for threads (currently only 'nnimap) 8988know how to search for threads (currently only 'nnimap) a
9130a non-numeric prefix arg will use nnir to search the entire 8989non-numeric prefix arg will search the entire
9131server; without a prefix arg only the current group is 8990server; without a prefix arg only the current group is
9132searched. If the variable `gnus-refer-thread-use-nnir' is 8991searched. If the variable `gnus-refer-thread-use-search' is
9133non-nil the prefix arg has the reverse meaning. If no 8992non-nil the prefix arg has the reverse meaning. If no
9134backend-specific `request-thread' function is available fetch 8993backend-specific 'request-thread function is available fetch
9135LIMIT (the numerical prefix) old headers. If LIMIT is 8994LIMIT (the numerical prefix) old headers. If LIMIT is
9136non-numeric or nil fetch the number specified by the 8995non-numeric or nil fetch the number specified by the
9137`gnus-refer-thread-limit' variable." 8996`gnus-refer-thread-limit' variable."
@@ -9141,9 +9000,9 @@ non-numeric or nil fetch the number specified by the
9141 (gnus-inhibit-demon t) 9000 (gnus-inhibit-demon t)
9142 (gnus-summary-ignore-duplicates t) 9001 (gnus-summary-ignore-duplicates t)
9143 (gnus-read-all-available-headers t) 9002 (gnus-read-all-available-headers t)
9144 (gnus-refer-thread-use-nnir 9003 (gnus-refer-thread-use-search
9145 (if (and (not (null limit)) (listp limit)) 9004 (if (and (not (null limit)) (listp limit))
9146 (not gnus-refer-thread-use-nnir) gnus-refer-thread-use-nnir)) 9005 (not gnus-refer-thread-use-search) gnus-refer-thread-use-search))
9147 (new-headers 9006 (new-headers
9148 (if (gnus-check-backend-function 9007 (if (gnus-check-backend-function
9149 'request-thread gnus-newsgroup-name) 9008 'request-thread gnus-newsgroup-name)
@@ -9284,9 +9143,9 @@ non-numeric or nil fetch the number specified by the
9284 (dolist (method gnus-refer-article-method) 9143 (dolist (method gnus-refer-article-method)
9285 (push (if (eq 'current method) 9144 (push (if (eq 'current method)
9286 gnus-current-select-method 9145 gnus-current-select-method
9287 (if (eq 'nnir (car method)) 9146 (if (eq 'nnselect (car method))
9288 (list 9147 (list
9289 'nnir 9148 'nnselect
9290 (or (cadr method) 9149 (or (cadr method)
9291 (gnus-method-to-server gnus-current-select-method))) 9150 (gnus-method-to-server gnus-current-select-method)))
9292 method)) 9151 method))
diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el
index 295395c79c2..4e3fc9868b4 100644
--- a/lisp/gnus/gnus.el
+++ b/lisp/gnus/gnus.el
@@ -1607,7 +1607,7 @@ total number of articles in the group.")
1607 :variable-default (mapcar 1607 :variable-default (mapcar
1608 (lambda (g) (list g t)) 1608 (lambda (g) (list g t))
1609 '("delayed$" "drafts$" "queue$" "INBOX$" 1609 '("delayed$" "drafts$" "queue$" "INBOX$"
1610 "^nnmairix:" "^nnir:" "archive")) 1610 "^nnmairix:" "^nnselect:" "archive"))
1611 :variable-document 1611 :variable-document
1612 "Groups in which the registry should be turned off." 1612 "Groups in which the registry should be turned off."
1613 :variable-group gnus-registry 1613 :variable-group gnus-registry
@@ -3153,7 +3153,10 @@ that that variable is buffer-local to the summary buffers."
3153 3153
3154(defun gnus-kill-ephemeral-group (group) 3154(defun gnus-kill-ephemeral-group (group)
3155 "Remove ephemeral GROUP from relevant structures." 3155 "Remove ephemeral GROUP from relevant structures."
3156 (remhash group gnus-newsrc-hashtb)) 3156 (remhash group gnus-newsrc-hashtb)
3157 (setq gnus-newsrc-alist
3158 (delq (assoc group gnus-newsrc-alist)
3159 gnus-newsrc-alist)))
3157 3160
3158(defun gnus-simplify-mode-line () 3161(defun gnus-simplify-mode-line ()
3159 "Make mode lines a bit simpler." 3162 "Make mode lines a bit simpler."
diff --git a/lisp/gnus/nndiary.el b/lisp/gnus/nndiary.el
index 945ef0351e5..7894285bdf3 100644
--- a/lisp/gnus/nndiary.el
+++ b/lisp/gnus/nndiary.el
@@ -992,7 +992,7 @@ all. This may very well take some time.")
992 (narrow-to-region 992 (narrow-to-region
993 (goto-char (point-min)) 993 (goto-char (point-min))
994 (if (search-forward "\n\n" nil t) (1- (point)) (point-max)))) 994 (if (search-forward "\n\n" nil t) (1- (point)) (point-max))))
995 (let ((headers (nnheader-parse-naked-head))) 995 (let ((headers (nnheader-parse-head t)))
996 (setf (mail-header-chars headers) chars) 996 (setf (mail-header-chars headers) chars)
997 (setf (mail-header-number headers) number) 997 (setf (mail-header-number headers) number)
998 headers)))) 998 headers))))
diff --git a/lisp/gnus/nnfolder.el b/lisp/gnus/nnfolder.el
index c27af1742d8..6ff99056d84 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."
1160 (if (search-forward "\n\n" e t) (setq e (1- (point))))) 1160 (if (search-forward "\n\n" e t) (setq e (1- (point)))))
1161 (with-temp-buffer 1161 (with-temp-buffer
1162 (insert-buffer-substring buf b e) 1162 (insert-buffer-substring buf b e)
1163 (let ((headers (nnheader-parse-naked-head))) 1163 (let ((headers (nnheader-parse-head t)))
1164 (setf (mail-header-chars headers) chars) 1164 (setf (mail-header-chars headers) chars)
1165 (setf (mail-header-number headers) number) 1165 (setf (mail-header-number headers) number)
1166 headers))))) 1166 headers)))))
diff --git a/lisp/gnus/nnheader.el b/lisp/gnus/nnheader.el
index fee7a169ff9..1a50697bf5d 100644
--- a/lisp/gnus/nnheader.el
+++ b/lisp/gnus/nnheader.el
@@ -28,6 +28,10 @@
28 28
29(eval-when-compile (require 'cl-lib)) 29(eval-when-compile (require 'cl-lib))
30 30
31(defvar gnus-decode-encoded-word-function)
32(defvar gnus-decode-encoded-address-function)
33(defvar gnus-alter-header-function)
34
31(defvar nnmail-extra-headers) 35(defvar nnmail-extra-headers)
32(defvar gnus-newsgroup-name) 36(defvar gnus-newsgroup-name)
33(defvar jka-compr-compression-info-list) 37(defvar jka-compr-compression-info-list)
@@ -39,6 +43,7 @@
39(require 'mail-utils) 43(require 'mail-utils)
40(require 'mm-util) 44(require 'mm-util)
41(require 'gnus-util) 45(require 'gnus-util)
46(autoload 'gnus-remove-odd-characters "gnus-sum")
42(autoload 'gnus-range-add "gnus-range") 47(autoload 'gnus-range-add "gnus-range")
43(autoload 'gnus-remove-from-range "gnus-range") 48(autoload 'gnus-remove-from-range "gnus-range")
44;; FIXME none of these are used explicitly in this file. 49;; FIXME none of these are used explicitly in this file.
@@ -188,124 +193,167 @@ on your system, you could say something like:
188 193
189(autoload 'ietf-drums-unfold-fws "ietf-drums") 194(autoload 'ietf-drums-unfold-fws "ietf-drums")
190 195
191(defun nnheader-parse-naked-head (&optional number) 196
192 ;; This function unfolds continuation lines in this buffer 197(defsubst nnheader-head-make-header (number)
193 ;; destructively. When this side effect is unwanted, use 198 "Using data of type 'head in the current buffer
194 ;; `nnheader-parse-head' instead of this function. 199 return a full mail header with article NUMBER."
195 (let ((case-fold-search t) 200 (let ((p (point-min))
196 (buffer-read-only nil)
197 (cur (current-buffer)) 201 (cur (current-buffer))
198 (p (point-min)) 202 in-reply-to chars lines end ref)
199 in-reply-to lines ref) 203 ;; This implementation of this function, with nine
200 (nnheader-remove-cr-followed-by-lf) 204 ;; search-forwards instead of the one re-search-forward and a
201 (ietf-drums-unfold-fws) 205 ;; case (which basically was the old function) is actually
202 (subst-char-in-region (point-min) (point-max) ?\t ? ) 206 ;; about twice as fast, even though it looks messier. You
203 (goto-char p) 207 ;; can't have everything, I guess. Speed and elegance don't
204 (insert "\n") 208 ;; always go hand in hand.
205 (prog1 209 (make-full-mail-header
206 ;; This implementation of this function, with nine 210 ;; Number.
207 ;; search-forwards instead of the one re-search-forward and a 211 number
208 ;; case (which basically was the old function) is actually 212 ;; Subject.
209 ;; about twice as fast, even though it looks messier. You 213 (progn
210 ;; can't have everything, I guess. Speed and elegance don't 214 (goto-char p)
211 ;; always go hand in hand. 215 (if (search-forward "\nsubject:" nil t)
212 (make-full-mail-header 216 (funcall gnus-decode-encoded-word-function
213 ;; Number. 217 (nnheader-header-value))
214 (or number 0) 218 "(none)"))
215 ;; Subject. 219 ;; From.
216 (progn 220 (progn
217 (goto-char p) 221 (goto-char p)
218 (if (search-forward "\nsubject:" nil t) 222 (if (search-forward "\nfrom:" nil t)
219 (nnheader-header-value) "(none)")) 223 (funcall gnus-decode-encoded-address-function
220 ;; From. 224 (nnheader-header-value))
221 (progn 225 "(nobody)"))
222 (goto-char p) 226 ;; Date.
223 (if (search-forward "\nfrom:" nil t) 227 (progn
224 (nnheader-header-value) "(nobody)")) 228 (goto-char p)
225 ;; Date. 229 (if (search-forward "\ndate:" nil t)
226 (progn 230 (nnheader-header-value) ""))
227 (goto-char p) 231 ;; Message-ID.
228 (if (search-forward "\ndate:" nil t) 232 (progn
229 (nnheader-header-value) "")) 233 (goto-char p)
230 ;; Message-ID. 234 (if (re-search-forward
231 (progn 235 "^message-id: *\\(<[^\n\t> ]+>\\)" nil t)
232 (goto-char p) 236 ;; We do it this way to make sure the Message-ID
233 (if (search-forward "\nmessage-id:" nil t) 237 ;; is (somewhat) syntactically valid.
234 (buffer-substring 238 (buffer-substring (match-beginning 1)
235 (1- (or (search-forward "<" (point-at-eol) t) 239 (match-end 1))
236 (point))) 240 ;; If there was no message-id, we just fake one to make
237 (or (search-forward ">" (point-at-eol) t) (point))) 241 ;; subsequent routines simpler.
238 ;; If there was no message-id, we just fake one to make 242 (nnheader-generate-fake-message-id number)))
239 ;; subsequent routines simpler. 243 ;; References.
240 (nnheader-generate-fake-message-id number))) 244 (progn
241 ;; References. 245 (goto-char p)
242 (progn 246 (if (search-forward "\nreferences:" nil t)
247 (progn
248 (setq end (point))
249 (prog1
250 (nnheader-header-value)
251 (setq ref
252 (buffer-substring
253 (progn
254 (end-of-line)
255 (search-backward ">" end t)
256 (1+ (point)))
257 (progn
258 (search-backward "<" end t)
259 (point))))))
260 ;; Get the references from the in-reply-to header if there
261 ;; were no references and the in-reply-to header looks
262 ;; promising.
263 (if (and (search-forward "\nin-reply-to:" nil t)
264 (setq in-reply-to (nnheader-header-value))
265 (string-match "<[^>]+>" in-reply-to))
266 (let (ref2)
267 (setq ref (substring in-reply-to (match-beginning 0)
268 (match-end 0)))
269 (while (string-match "<[^>]+>" in-reply-to (match-end 0))
270 (setq ref2 (substring in-reply-to (match-beginning 0)
271 (match-end 0)))
272 (when (> (length ref2) (length ref))
273 (setq ref ref2)))
274 ref)
275 nil)))
276 ;; Chars.
277 (progn
278 (goto-char p)
279 (if (search-forward "\nchars: " nil t)
280 (if (numberp (setq chars (ignore-errors (read cur))))
281 chars -1)
282 -1))
283 ;; Lines.
284 (progn
285 (goto-char p)
286 (if (search-forward "\nlines: " nil t)
287 (if (numberp (setq lines (ignore-errors (read cur))))
288 lines -1)
289 -1))
290 ;; Xref.
291 (progn
292 (goto-char p)
293 (and (search-forward "\nxref:" nil t)
294 (nnheader-header-value)))
295 ;; Extra.
296 (when nnmail-extra-headers
297 (let ((extra nnmail-extra-headers)
298 out)
299 (while extra
243 (goto-char p) 300 (goto-char p)
244 (if (search-forward "\nreferences:" nil t) 301 (when (search-forward
245 (nnheader-header-value) 302 (concat "\n" (symbol-name (car extra)) ":") nil t)
246 ;; Get the references from the in-reply-to header if 303 (push (cons (car extra) (nnheader-header-value))
247 ;; there were no references and the in-reply-to header 304 out))
248 ;; looks promising. 305 (pop extra))
249 (if (and (search-forward "\nin-reply-to:" nil t) 306 out)))))
250 (setq in-reply-to (nnheader-header-value)) 307
251 (string-match "<[^\n>]+>" in-reply-to)) 308(defun nnheader-parse-head (&optional naked temp)
252 (let (ref2) 309 "Parse data of type 'header in the current buffer and return a
253 (setq ref (substring in-reply-to (match-beginning 0) 310 mail header, modifying the buffer contents in the process. The
254 (match-end 0))) 311 buffer is assumed to begin each header with an \"Article
255 (while (string-match "<[^\n>]+>" 312 retrieved\" line with an article number; If NAKED is non-nil
256 in-reply-to (match-end 0)) 313 this line is assumed absent, and the buffer should contain a
257 (setq ref2 (substring in-reply-to (match-beginning 0) 314 single header's worth of data. If TEMP is non-nil the data is
258 (match-end 0))) 315 first copied to a temporary buffer leaving the original buffer
259 (when (> (length ref2) (length ref)) 316 untouched."
260 (setq ref ref2))) 317 (let ((cur (current-buffer))
261 ref) 318 (num 0)
262 nil))) 319 (beg (point-min))
263 ;; Chars. 320 (end (point-max))
264 0 321 buf)
265 ;; Lines. 322 (when (or naked
266 (progn 323 ;; Search to the beginning of the next header. Error
267 (goto-char p) 324 ;; messages do not begin with 2 or 3.
268 (if (search-forward "\nlines: " nil t) 325 (when (re-search-forward "^[23][0-9]+ " nil t)
269 (if (numberp (setq lines (read cur))) 326 (setq num (read cur)
270 lines 0) 327 beg (point)
271 0)) 328 end (if (search-forward "\n.\n" nil t)
272 ;; Xref. 329 (goto-char (- (point) 2))
273 (progn 330 (point)))))
274 (goto-char p) 331 ;; When TEMP copy the data to a temporary buffer
275 (and (search-forward "\nxref:" nil t) 332 (if temp
276 (nnheader-header-value))) 333 (progn
277 ;; Extra. 334 (set-buffer (setq buf (generate-new-buffer " *nnheader-temp*")))
278 (when nnmail-extra-headers 335 (insert-buffer-substring cur beg end))
279 (let ((extra nnmail-extra-headers) 336 ;; Otherwise just narrow to the data
280 out) 337 (narrow-to-region beg end))
281 (while extra 338 (let ((case-fold-search t)
282 (goto-char p) 339 (buffer-read-only nil)
283 (when (search-forward 340 header)
284 (concat "\n" (symbol-name (car extra)) ":") nil t) 341 (nnheader-remove-cr-followed-by-lf)
285 (push (cons (car extra) (nnheader-header-value)) 342 (ietf-drums-unfold-fws)
286 out)) 343 (subst-char-in-region (point-min) (point-max) ?\t ? t)
287 (pop extra)) 344 (subst-char-in-region (point-min) (point-max) ?\r ? t)
288 out))) 345 (goto-char (point-min))
289 (goto-char p) 346 (insert "\n")
290 (delete-char 1)))) 347 (setq header (nnheader-head-make-header num))
291 348 (goto-char (point-min))
292(defun nnheader-parse-head (&optional naked) 349 (delete-char 1)
293 (let ((cur (current-buffer)) num beg end) 350 (if temp
294 (when (if naked 351 (kill-buffer buf)
295 (setq num 0 352 (goto-char (point-max))
296 beg (point-min) 353 (widen))
297 end (point-max)) 354 (when gnus-alter-header-function
298 ;; Search to the beginning of the next header. Error 355 (funcall gnus-alter-header-function header))
299 ;; messages do not begin with 2 or 3. 356 header))))
300 (when (re-search-forward "^[23][0-9]+ " nil t)
301 (setq num (read cur)
302 beg (point)
303 end (if (search-forward "\n.\n" nil t)
304 (goto-char (- (point) 2))
305 (point)))))
306 (with-temp-buffer
307 (insert-buffer-substring cur beg end)
308 (nnheader-parse-naked-head num)))))
309 357
310(defmacro nnheader-nov-skip-field () 358(defmacro nnheader-nov-skip-field ()
311 '(search-forward "\t" eol 'move)) 359 '(search-forward "\t" eol 'move))
@@ -347,24 +395,43 @@ on your system, you could say something like:
347 'id) 395 'id)
348 (nnheader-generate-fake-message-id ,number)))) 396 (nnheader-generate-fake-message-id ,number))))
349 397
350(defun nnheader-parse-nov () 398(defalias 'nnheader-nov-make-header 'nnheader-parse-nov)
399(autoload 'gnus-extract-message-id-from-in-reply-to "gnus-sum")
400
401(defun nnheader-parse-nov (&optional number)
351 (let ((eol (point-at-eol)) 402 (let ((eol (point-at-eol))
352 (number (nnheader-nov-read-integer))) 403 references in-reply-to x header)
353 (vector 404 (setq header
354 number ; number 405 (make-full-mail-header
355 (nnheader-nov-field) ; subject 406 (or number (nnheader-nov-read-integer)) ; number
356 (nnheader-nov-field) ; from 407 (condition-case () ; subject
357 (nnheader-nov-field) ; date 408 (gnus-remove-odd-characters
358 (nnheader-nov-read-message-id number) ; id 409 (funcall gnus-decode-encoded-word-function
359 (nnheader-nov-field) ; refs 410 (setq x (nnheader-nov-field))))
360 (nnheader-nov-read-integer) ; chars 411 (error x))
361 (nnheader-nov-read-integer) ; lines 412 (condition-case () ; from
362 (if (eq (char-after) ?\n) 413 (gnus-remove-odd-characters
363 nil 414 (funcall gnus-decode-encoded-address-function
364 (if (looking-at "Xref: ") 415 (setq x (nnheader-nov-field))))
365 (goto-char (match-end 0))) 416 (error x))
366 (nnheader-nov-field)) ; Xref 417 (nnheader-nov-field) ; date
367 (nnheader-nov-parse-extra)))) ; extra 418 (nnheader-nov-read-message-id number) ; id
419 (setq references (nnheader-nov-field)) ; refs
420 (nnheader-nov-read-integer) ; chars
421 (nnheader-nov-read-integer) ; lines
422 (unless (eobp)
423 (if (looking-at "Xref: ")
424 (goto-char (match-end 0)))
425 (nnheader-nov-field)) ; Xref
426 (nnheader-nov-parse-extra))) ; extra
427
428 (when (and (string= references "")
429 (setq in-reply-to (mail-header-extra header))
430 (setq in-reply-to (cdr (assq 'In-Reply-To in-reply-to))))
431 (setf (mail-header-references header)
432 (gnus-extract-message-id-from-in-reply-to in-reply-to)))
433 header))
434
368 435
369(defun nnheader-insert-nov (header) 436(defun nnheader-insert-nov (header)
370 (princ (mail-header-number header) (current-buffer)) 437 (princ (mail-header-number header) (current-buffer))
@@ -399,17 +466,6 @@ on your system, you could say something like:
399 (delete-char 1)) 466 (delete-char 1))
400 (forward-line 1))) 467 (forward-line 1)))
401 468
402(defun nnheader-parse-overview-file (file)
403 "Parse FILE and return a list of headers."
404 (mm-with-unibyte-buffer
405 (nnheader-insert-file-contents file)
406 (goto-char (point-min))
407 (let (headers)
408 (while (not (eobp))
409 (push (nnheader-parse-nov) headers)
410 (forward-line 1))
411 (nreverse headers))))
412
413(defun nnheader-write-overview-file (file headers) 469(defun nnheader-write-overview-file (file headers)
414 "Write HEADERS to FILE." 470 "Write HEADERS to FILE."
415 (with-temp-file file 471 (with-temp-file file
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index 507e12a55e7..d797e893f51 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."
1686 (gnus-add-to-range 1686 (gnus-add-to-range
1687 (gnus-add-to-range 1687 (gnus-add-to-range
1688 (gnus-range-add (gnus-info-read info) 1688 (gnus-range-add (gnus-info-read info)
1689 vanished) 1689 vanished)
1690 (cdr (assq '%Flagged flags))) 1690 (cdr (assq '%Flagged flags)))
1691 (cdr (assq '%Seen flags)))) 1691 (cdr (assq '%Seen flags))))
1692 (let ((marks (gnus-info-marks info))) 1692 (let ((marks (gnus-info-marks info)))
@@ -1851,15 +1851,15 @@ If LIMIT, first try to limit the search to the N last articles."
1851 (setq nnimap-status-string "Read-only server") 1851 (setq nnimap-status-string "Read-only server")
1852 nil) 1852 nil)
1853 1853
1854(defvar gnus-refer-thread-use-nnir) ;; gnus-sum.el 1854(defvar gnus-refer-thread-use-search) ;; gnus-sum.el
1855(declare-function gnus-fetch-headers "gnus-sum" 1855(declare-function gnus-fetch-headers "gnus-sum"
1856 (articles &optional limit force-new dependencies)) 1856 (articles &optional limit force-new dependencies))
1857 1857
1858(autoload 'nnir-search-thread "nnir") 1858(autoload 'nnselect-search-thread "nnselect")
1859 1859
1860(deffoo nnimap-request-thread (header &optional group server) 1860(deffoo nnimap-request-thread (header &optional group server)
1861 (if gnus-refer-thread-use-nnir 1861 (if gnus-refer-thread-use-search
1862 (nnir-search-thread header) 1862 (nnselect-search-thread header)
1863 (when (nnimap-change-group group server) 1863 (when (nnimap-change-group group server)
1864 (let* ((cmd (nnimap-make-thread-query header)) 1864 (let* ((cmd (nnimap-make-thread-query header))
1865 (result (with-current-buffer (nnimap-buffer) 1865 (result (with-current-buffer (nnimap-buffer)
diff --git a/lisp/gnus/nnir.el b/lisp/gnus/nnir.el
index 722969c21ba..2ec39cf34c9 100644
--- a/lisp/gnus/nnir.el
+++ b/lisp/gnus/nnir.el
@@ -10,6 +10,7 @@
10;; IMAP search improved by Daniel Pittman <daniel@rimspace.net>. 10;; IMAP search improved by Daniel Pittman <daniel@rimspace.net>.
11;; nnmaildir support for Swish++ and Namazu backends by: 11;; nnmaildir support for Swish++ and Namazu backends by:
12;; Justus Piater <Justus <at> Piater.name> 12;; Justus Piater <Justus <at> Piater.name>
13;; Mostly rewritten by Andrew Cohen <cohen@bu.edu> from 2010
13;; Keywords: news mail searching ir 14;; Keywords: news mail searching ir
14 15
15;; This file is part of GNU Emacs. 16;; This file is part of GNU Emacs.
@@ -29,20 +30,11 @@
29 30
30;;; Commentary: 31;;; Commentary:
31 32
32;; What does it do? Well, it allows you to search your mail using 33;; What does it do? Well, it searches your mail using some search
33;; some search engine (imap, namazu, swish-e and others -- see 34;; engine (imap, namazu, swish-e, gmane and others -- see later).
34;; later) by typing `G G' in the Group buffer. You will then get a
35;; buffer which shows all articles matching the query, sorted by
36;; Retrieval Status Value (score).
37
38;; When looking at the retrieval result (in the Summary buffer) you
39;; can type `A W' (aka M-x gnus-warp-to-article RET) on an article. You
40;; will be warped into the group this article came from. Typing `A T'
41;; (aka M-x gnus-summary-refer-thread RET) will warp to the group and
42;; also show the thread this article is part of.
43 35
44;; The Lisp setup may involve setting a few variables and setting up the 36;; The Lisp setup may involve setting a few variables and setting up the
45;; search engine. You can define the variables in the server definition 37;; search engine. You can define the variables in the server definition
46;; like this : 38;; like this :
47;; (setq gnus-secondary-select-methods '( 39;; (setq gnus-secondary-select-methods '(
48;; (nnimap "" (nnimap-address "localhost") 40;; (nnimap "" (nnimap-address "localhost")
@@ -53,6 +45,45 @@
53;; an alist, type `C-h v nnir-engines RET' for more information; this 45;; an alist, type `C-h v nnir-engines RET' for more information; this
54;; includes examples for setting `nnir-search-engine', too.) 46;; includes examples for setting `nnir-search-engine', too.)
55 47
48;; The entry to searching is the single function `nnir-run-query',
49;; which dispatches the search to the proper search function. The
50;; argument of `nnir-run-query' is an alist with two keys:
51;; 'nnir-query-spec and 'nnir-group-spec. The value for
52;; 'nnir-query-spec is an alist. The only required key/value pair is
53;; (query . "query") specifying the search string to pass to the query
54;; engine. Individual engines may have other elements. The value of
55;; 'nnir-group-spec is a list with the specification of the
56;; groups/servers to search. The format of the 'nnir-group-spec is
57;; (("server1" ("group11" "group12")) ("server2" ("group21"
58;; "group22"))). If any of the group lists is absent then all groups
59;; on that server are searched.
60
61;; The output of `nnir-run-query' is a vector, each element of which
62;; should in turn be a three-element vector with the form: [fully
63;; prefixed group-name of the article; the article number; the
64;; Retrieval Status Value (RSV)] as returned from the search engine.
65;; An RSV is the score assigned to the document by the search engine.
66;; For Boolean search engines, the RSV is always 1000 (or 1 or 100, or
67;; whatever you like).
68
69;; A vector of this form is used by the nnselect backend to create
70;; virtual groups. So nnir-run-query is a suitable function to use in
71;; nnselect groups.
72
73;; The default sorting order of articles in an nnselect summary buffer
74;; is based on the order of the articles in the above mentioned
75;; vector, so that's where you can do the sorting you'd like. Maybe
76;; it would be nice to have a way of displaying the search result
77;; sorted differently?
78
79;; So what do you need to do when you want to add another search
80;; engine? You write a function that executes the query. Temporary
81;; data from the search engine can be put in `nnir-tmp-buffer'. This
82;; function should return the list of articles as a vector, as
83;; described above. Then, you need to register this backend in
84;; `nnir-engines'. Then, users can choose the backend by setting
85;; `nnir-search-engine' as a server variable.
86
56;; If you use one of the local indices (namazu, find-grep, swish) you 87;; If you use one of the local indices (namazu, find-grep, swish) you
57;; must also set up a search engine backend. 88;; must also set up a search engine backend.
58 89
@@ -75,13 +106,13 @@
75;; ,---- 106;; ,----
76;; | package conf; # Don't remove this line! 107;; | package conf; # Don't remove this line!
77;; | 108;; |
78;; | # Paths which will not be indexed. Don't use `^' or `$' anchors. 109;; | # Paths which will not be indexed. Don't use `^' or `$' anchors.
79;; | $EXCLUDE_PATH = "spam|sent"; 110;; | $EXCLUDE_PATH = "spam|sent";
80;; | 111;; |
81;; | # Header fields which should be searchable. case-insensitive 112;; | # Header fields which should be searchable. case-insensitive
82;; | $REMAIN_HEADER = "from|date|message-id|subject"; 113;; | $REMAIN_HEADER = "from|date|message-id|subject";
83;; | 114;; |
84;; | # Searchable fields. case-insensitive 115;; | # Searchable fields. case-insensitive
85;; | $SEARCH_FIELD = "from|date|message-id|subject"; 116;; | $SEARCH_FIELD = "from|date|message-id|subject";
86;; | 117;; |
87;; | # The max length of a word. 118;; | # The max length of a word.
@@ -121,72 +152,17 @@
121;; | (nnml-active-file "~/News/cache/active")) 152;; | (nnml-active-file "~/News/cache/active"))
122;; `---- 153;; `----
123 154
124;; Developer information:
125
126;; I have tried to make the code expandable. Basically, it is divided
127;; into two layers. The upper layer is somewhat like the `nnvirtual'
128;; backend: given a specification of what articles to show from
129;; another backend, it creates a group containing exactly those
130;; articles. The lower layer issues a query to a search engine and
131;; produces such a specification of what articles to show from the
132;; other backend.
133
134;; The interface between the two layers consists of the single
135;; function `nnir-run-query', which dispatches the search to the
136;; proper search function. The argument of `nnir-run-query' is an
137;; alist with two keys: 'nnir-query-spec and 'nnir-group-spec. The
138;; value for 'nnir-query-spec is an alist. The only required key/value
139;; pair is (query . "query") specifying the search string to pass to
140;; the query engine. Individual engines may have other elements. The
141;; value of 'nnir-group-spec is a list with the specification of the
142;; groups/servers to search. The format of the 'nnir-group-spec is
143;; (("server1" ("group11" "group12")) ("server2" ("group21"
144;; "group22"))). If any of the group lists is absent then all groups
145;; on that server are searched.
146
147;; The output of `nnir-run-query' is supposed to be a vector, each
148;; element of which should in turn be a three-element vector. The
149;; first element should be full group name of the article, the second
150;; element should be the article number, and the third element should
151;; be the Retrieval Status Value (RSV) as returned from the search
152;; engine. An RSV is the score assigned to the document by the search
153;; engine. For Boolean search engines, the RSV is always 1000 (or 1
154;; or 100, or whatever you like).
155
156;; The sorting order of the articles in the summary buffer created by
157;; nnir is based on the order of the articles in the above mentioned
158;; vector, so that's where you can do the sorting you'd like. Maybe
159;; it would be nice to have a way of displaying the search result
160;; sorted differently?
161
162;; So what do you need to do when you want to add another search
163;; engine? You write a function that executes the query. Temporary
164;; data from the search engine can be put in `nnir-tmp-buffer'. This
165;; function should return the list of articles as a vector, as
166;; described above. Then, you need to register this backend in
167;; `nnir-engines'. Then, users can choose the backend by setting
168;; `nnir-search-engine' as a server variable.
169 155
170;;; Code: 156;;; Code:
171 157
172;;; Setup: 158;;; Setup:
173 159
174(require 'nnoo)
175(require 'gnus-group)
176(require 'message)
177(require 'gnus-util)
178(eval-when-compile (require 'cl-lib)) 160(eval-when-compile (require 'cl-lib))
161(require 'gnus)
179 162
180;;; Internal Variables: 163;;; Internal Variables:
181 164
182(defvar nnir-memo-query nil 165(defvar gnus-inhibit-demon)
183 "Internal: stores current query.")
184
185(defvar nnir-memo-server nil
186 "Internal: stores current server.")
187
188(defvar nnir-artlist nil
189 "Internal: stores search result.")
190 166
191(defvar nnir-search-history () 167(defvar nnir-search-history ()
192 "Internal: the history for querying search options in nnir.") 168 "Internal: the history for querying search options in nnir.")
@@ -203,30 +179,19 @@
203 ("to" . "TO") 179 ("to" . "TO")
204 ("from" . "FROM") 180 ("from" . "FROM")
205 ("body" . "BODY") 181 ("body" . "BODY")
206 ("imap" . "")) 182 ("imap" . "")
183 ("gmail" . "X-GM-RAW"))
207 "Mapping from user readable keys to IMAP search items for use in nnir.") 184 "Mapping from user readable keys to IMAP search items for use in nnir.")
208 185
209(defvar nnir-imap-search-other "HEADER %S" 186(defvar nnir-imap-search-other "HEADER %S"
210 "The IMAP search item to use for anything other than 187 "The IMAP search item for anything other than `nnir-imap-search-arguments'.
211`nnir-imap-search-arguments'. By default this is the name of an 188By default this is the name of an email header field.")
212email header field.")
213 189
214(defvar nnir-imap-search-argument-history () 190(defvar nnir-imap-search-argument-history ()
215 "The history for querying search options in nnir.") 191 "The history for querying search options in nnir.")
216 192
217;;; Helper macros 193;;; Helper macros
218 194
219;; Data type article list.
220
221(defmacro nnir-artlist-length (artlist)
222 "Return number of articles in artlist."
223 `(length ,artlist))
224
225(defmacro nnir-artlist-article (artlist n)
226 "Return from ARTLIST the Nth artitem (counting starting at 1)."
227 `(when (> ,n 0)
228 (elt ,artlist (1- ,n))))
229
230(defmacro nnir-artitem-group (artitem) 195(defmacro nnir-artitem-group (artitem)
231 "Return the group from the ARTITEM." 196 "Return the group from the ARTITEM."
232 `(elt ,artitem 0)) 197 `(elt ,artitem 0))
@@ -239,52 +204,6 @@ email header field.")
239 "Return the Retrieval Status Value (RSV, score) from the ARTITEM." 204 "Return the Retrieval Status Value (RSV, score) from the ARTITEM."
240 `(elt ,artitem 2)) 205 `(elt ,artitem 2))
241 206
242(defmacro nnir-article-group (article)
243 "Return the group for ARTICLE."
244 `(nnir-artitem-group (nnir-artlist-article nnir-artlist ,article)))
245
246(defmacro nnir-article-number (article)
247 "Return the number for ARTICLE."
248 `(nnir-artitem-number (nnir-artlist-article nnir-artlist ,article)))
249
250(defmacro nnir-article-rsv (article)
251 "Return the rsv for ARTICLE."
252 `(nnir-artitem-rsv (nnir-artlist-article nnir-artlist ,article)))
253
254(defsubst nnir-article-ids (article)
255 "Return the pair `(nnir id . real id)' of ARTICLE."
256 (cons article (nnir-article-number article)))
257
258(defmacro nnir-categorize (sequence keyfunc &optional valuefunc)
259 "Sort a SEQUENCE into categories and returns a list of the form
260`((key1 (element11 element12)) (key2 (element21 element22))'.
261The category key for a member of the sequence is obtained
262as `(KEYFUNC member)' and the corresponding element is just
263`member'. If VALUEFUNC is non-nil, the element of the list
264is `(VALUEFUNC member)'."
265 `(unless (null ,sequence)
266 (let (value)
267 (mapc
268 (lambda (member)
269 (let ((y (,keyfunc member))
270 (x ,(if valuefunc
271 `(,valuefunc member)
272 'member)))
273 (if (assoc y value)
274 (push x (cadr (assoc y value)))
275 (push (list y (list x)) value))))
276 ,sequence)
277 value)))
278
279;;; Finish setup:
280
281(require 'gnus-sum)
282
283(nnoo-declare nnir)
284(nnoo-define-basics nnir)
285
286(gnus-declare-backend "nnir" 'mail 'virtual)
287
288 207
289;;; User Customizable Variables: 208;;; User Customizable Variables:
290 209
@@ -293,43 +212,17 @@ is `(VALUEFUNC member)'."
293 :group 'gnus) 212 :group 'gnus)
294 213
295(defcustom nnir-ignored-newsgroups "" 214(defcustom nnir-ignored-newsgroups ""
296 "A regexp to match newsgroups in the active file that should 215 "Newsgroups to skip when searching.
297be skipped when searching." 216Any newsgroup in the active file matching this regexp will be
217skipped when searching."
298 :version "24.1" 218 :version "24.1"
299 :type '(regexp) 219 :type '(regexp)
300 :group 'nnir) 220 :group 'nnir)
301 221
302(defcustom nnir-summary-line-format nil
303 "The format specification of the lines in an nnir summary buffer.
304
305All the items from `gnus-summary-line-format' are available, along
306with three items unique to nnir summary buffers:
307
308%Z Search retrieval score value (integer)
309%G Article original full group name (string)
310%g Article original short group name (string)
311
312If nil this will use `gnus-summary-line-format'."
313 :version "24.1"
314 :type '(choice (const :tag "gnus-summary-line-format" nil) string)
315 :group 'nnir)
316
317(defcustom nnir-retrieve-headers-override-function nil
318 "If non-nil, a function that accepts an article list and group
319and populates the `nntp-server-buffer' with the retrieved
320headers. Must return either `nov' or `headers' indicating the
321retrieved header format.
322
323If this variable is nil, or if the provided function returns nil for
324a search result, `gnus-retrieve-headers' will be called instead."
325 :version "24.1"
326 :type '(choice (const :tag "gnus-retrieve-headers" nil) function)
327 :group 'nnir)
328
329(defcustom nnir-imap-default-search-key "whole message" 222(defcustom nnir-imap-default-search-key "whole message"
330 "The default IMAP search key for an nnir search. Must be one of 223 "The default IMAP search key for an nnir search.
331the keys in `nnir-imap-search-arguments'. To use raw imap queries 224Must be one of the keys in `nnir-imap-search-arguments'. To use
332by default set this to \"imap\"." 225raw imap queries by default set this to \"imap\"."
333 :version "24.1" 226 :version "24.1"
334 :type `(choice ,@(mapcar (lambda (elem) (list 'const (car elem))) 227 :type `(choice ,@(mapcar (lambda (elem) (list 'const (car elem)))
335 nnir-imap-search-arguments)) 228 nnir-imap-search-arguments))
@@ -357,9 +250,9 @@ Instead, use this:
357 :group 'nnir) 250 :group 'nnir)
358 251
359(defcustom nnir-swish++-remove-prefix (concat (getenv "HOME") "/Mail/") 252(defcustom nnir-swish++-remove-prefix (concat (getenv "HOME") "/Mail/")
360 "The prefix to remove from each file name returned by swish++ 253 "The prefix to remove from swish++ file names to get group names.
361in order to get a group name (albeit with / instead of .). This is a 254Resulting names have '/' in place of '.'. This is a regular
362regular expression. 255expression.
363 256
364This variable is very similar to `nnir-namazu-remove-prefix', except 257This variable is very similar to `nnir-namazu-remove-prefix', except
365that it is for swish++, not Namazu." 258that it is for swish++, not Namazu."
@@ -408,9 +301,9 @@ This could be a server parameter."
408 :group 'nnir) 301 :group 'nnir)
409 302
410(defcustom nnir-swish-e-remove-prefix (concat (getenv "HOME") "/Mail/") 303(defcustom nnir-swish-e-remove-prefix (concat (getenv "HOME") "/Mail/")
411 "The prefix to remove from each file name returned by swish-e 304 "The prefix to remove from swish-e file names to get group names.
412in order to get a group name (albeit with / instead of .). This is a 305Resulting names have '/' in place of '.'. This is a regular
413regular expression. 306expression.
414 307
415This variable is very similar to `nnir-namazu-remove-prefix', except 308This variable is very similar to `nnir-namazu-remove-prefix', except
416that it is for swish-e, not Namazu. 309that it is for swish-e, not Namazu.
@@ -441,8 +334,8 @@ Instead, use this:
441 :group 'nnir) 334 :group 'nnir)
442 335
443(defcustom nnir-hyrex-remove-prefix (concat (getenv "HOME") "/Mail/") 336(defcustom nnir-hyrex-remove-prefix (concat (getenv "HOME") "/Mail/")
444 "The prefix to remove from each file name returned by HyREX 337 "The prefix to remove from HyREX file names to get group names.
445in order to get a group name (albeit with / instead of .). 338Restulting names have '/' in place of '.'.
446 339
447For example, suppose that HyREX returns file names such as 340For example, suppose that HyREX returns file names such as
448\"/home/john/Mail/mail/misc/42\". For this example, use the following 341\"/home/john/Mail/mail/misc/42\". For this example, use the following
@@ -478,8 +371,8 @@ Instead, use this:
478 :group 'nnir) 371 :group 'nnir)
479 372
480(defcustom nnir-namazu-remove-prefix (concat (getenv "HOME") "/Mail/") 373(defcustom nnir-namazu-remove-prefix (concat (getenv "HOME") "/Mail/")
481 "The prefix to remove from each file name returned by Namazu 374 "The prefix to remove from Namazu file names to get group names.
482in order to get a group name (albeit with / instead of .). 375Resulting names have '/' in place of '.'.
483 376
484For example, suppose that Namazu returns file names such as 377For example, suppose that Namazu returns file names such as
485\"/home/john/Mail/mail/misc/42\". For this example, use the following 378\"/home/john/Mail/mail/misc/42\". For this example, use the following
@@ -509,9 +402,9 @@ Instead, use this:
509 402
510(defcustom nnir-notmuch-remove-prefix 403(defcustom nnir-notmuch-remove-prefix
511 (regexp-quote (or (getenv "MAILDIR") (expand-file-name "~/Mail"))) 404 (regexp-quote (or (getenv "MAILDIR") (expand-file-name "~/Mail")))
512 "The prefix to remove from each file name returned by notmuch 405 "The prefix to remove from notmuch file names to get group names.
513in order to get a group name (albeit with / instead of .). This is a 406Resulting names have '/' in place of '.'. This is a regular
514regular expression. 407expression.
515 408
516This variable is very similar to `nnir-namazu-remove-prefix', except 409This variable is very similar to `nnir-namazu-remove-prefix', except
517that it is for notmuch, not Namazu." 410that it is for notmuch, not Namazu."
@@ -590,347 +483,12 @@ Add an entry here when adding a new search engine.")
590 ,@(mapcar (lambda (elem) (list 'const (car elem))) 483 ,@(mapcar (lambda (elem) (list 'const (car elem)))
591 nnir-engines))))) 484 nnir-engines)))))
592 485
593;; Gnus glue.
594
595(declare-function gnus-group-topic-name "gnus-topic" ())
596(declare-function gnus-topic-find-groups "gnus-topic"
597 (topic &optional level all lowest recursive))
598
599(defun gnus-group-make-nnir-group (nnir-extra-parms &optional specs)
600 "Create an nnir group.
601Prompt for a search query and determine the groups to search as
602follows: if called from the *Server* buffer search all groups
603belonging to the server on the current line; if called from the
604*Group* buffer search any marked groups, or the group on the current
605line, or all the groups under the current topic. Calling with a
606prefix-arg prompts for additional search-engine specific constraints.
607A non-nil `specs' arg must be an alist with `nnir-query-spec' and
608`nnir-group-spec' keys, and skips all prompting."
609 (interactive "P")
610 (let* ((group-spec
611 (or (cdr (assq 'nnir-group-spec specs))
612 (if (gnus-server-server-name)
613 (list (list (gnus-server-server-name)))
614 (nnir-categorize
615 (or gnus-group-marked
616 (if (gnus-group-group-name)
617 (list (gnus-group-group-name))
618 (mapcar (lambda (entry)
619 (gnus-info-group (cadr entry)))
620 (gnus-topic-find-groups (gnus-group-topic-name)
621 nil t nil t))))
622 gnus-group-server))))
623 (query-spec
624 (or (cdr (assq 'nnir-query-spec specs))
625 (apply
626 'append
627 (list (cons 'query
628 (read-string "Query: " nil 'nnir-search-history)))
629 (when nnir-extra-parms
630 (mapcar
631 (lambda (x)
632 (nnir-read-parms (nnir-server-to-search-engine (car x))))
633 group-spec))))))
634 (gnus-group-read-ephemeral-group
635 (concat "nnir-" (message-unique-id))
636 (list 'nnir "nnir")
637 nil
638; (cons (current-buffer) gnus-current-window-configuration)
639 nil
640 nil nil
641 (list
642 (cons 'nnir-specs (list (cons 'nnir-query-spec query-spec)
643 (cons 'nnir-group-spec group-spec)))
644 (cons 'nnir-artlist nil)))))
645
646(defun gnus-summary-make-nnir-group (nnir-extra-parms)
647 "Search a group from the summary buffer."
648 (interactive "P")
649 (gnus-warp-to-article)
650 (let ((spec
651 (list
652 (cons 'nnir-group-spec
653 (list (list
654 (gnus-group-server gnus-newsgroup-name)
655 (list gnus-newsgroup-name)))))))
656 (gnus-group-make-nnir-group nnir-extra-parms spec)))
657
658
659;; Gnus backend interface functions.
660
661(deffoo nnir-open-server (server &optional definitions)
662 ;; Just set the server variables appropriately.
663 (let ((backend (car (gnus-server-to-method server))))
664 (if backend
665 (nnoo-change-server backend server definitions)
666 (add-hook 'gnus-summary-generate-hook 'nnir-mode)
667 (nnoo-change-server 'nnir server definitions))))
668
669(deffoo nnir-request-group (group &optional server dont-check _info)
670 (nnir-possibly-change-group group server)
671 (let ((pgroup (gnus-group-guess-full-name-from-command-method group))
672 length)
673 ;; Check for cached search result or run the query and cache the
674 ;; result.
675 (unless (and nnir-artlist dont-check)
676 (gnus-group-set-parameter
677 pgroup 'nnir-artlist
678 (setq nnir-artlist
679 (nnir-run-query
680 (gnus-group-get-parameter pgroup 'nnir-specs t))))
681 (nnir-request-update-info pgroup (gnus-get-info pgroup)))
682 (with-current-buffer nntp-server-buffer
683 (if (zerop (setq length (nnir-artlist-length nnir-artlist)))
684 (progn
685 (nnir-close-group group)
686 (nnheader-report 'nnir "Search produced empty results."))
687 (nnheader-insert "211 %d %d %d %s\n"
688 length ; total #
689 1 ; first #
690 length ; last #
691 group)))) ; group name
692 nnir-artlist)
693
694(defvar gnus-inhibit-demon)
695
696(deffoo nnir-retrieve-headers (articles &optional _group _server _fetch-old)
697 (with-current-buffer nntp-server-buffer
698 (let ((gnus-inhibit-demon t)
699 (articles-by-group (nnir-categorize
700 articles nnir-article-group nnir-article-ids))
701 headers)
702 (while (not (null articles-by-group))
703 (let* ((group-articles (pop articles-by-group))
704 (artgroup (car group-articles))
705 (articleids (cadr group-articles))
706 (artlist (sort (mapcar 'cdr articleids) '<))
707 (server (gnus-group-server artgroup))
708 (gnus-override-method (gnus-server-to-method server))
709 parsefunc)
710 ;; (nnir-possibly-change-group nil server)
711 (erase-buffer)
712 (pcase (setq gnus-headers-retrieved-by
713 (or
714 (and
715 nnir-retrieve-headers-override-function
716 (funcall nnir-retrieve-headers-override-function
717 artlist artgroup))
718 (gnus-retrieve-headers artlist artgroup nil)))
719 ('nov
720 (setq parsefunc 'nnheader-parse-nov))
721 ('headers
722 (setq parsefunc 'nnheader-parse-head))
723 (_ (error "Unknown header type %s while requesting articles \
724 of group %s" gnus-headers-retrieved-by artgroup)))
725 (goto-char (point-min))
726 (while (not (eobp))
727 (let* ((novitem (funcall parsefunc))
728 (artno (and novitem
729 (mail-header-number novitem)))
730 (art (car (rassq artno articleids))))
731 (when art
732 (setf (mail-header-number novitem) art)
733 (push novitem headers))
734 (forward-line 1)))))
735 (setq headers
736 (sort headers
737 (lambda (x y)
738 (< (mail-header-number x) (mail-header-number y)))))
739 (erase-buffer)
740 (mapc 'nnheader-insert-nov headers)
741 'nov)))
742
743(defvar gnus-article-decode-hook)
744
745(deffoo nnir-request-article (article &optional group server to-buffer)
746 (nnir-possibly-change-group group server)
747 (if (and (stringp article)
748 (not (eq 'nnimap (car (gnus-server-to-method server)))))
749 (nnheader-report
750 'nnir
751 "nnir-request-article only groks message ids for nnimap servers: %s"
752 server)
753 (save-excursion
754 (let ((article article)
755 query)
756 (when (stringp article)
757 (setq gnus-override-method (gnus-server-to-method server))
758 (setq query
759 (list
760 (cons 'query (format "HEADER Message-ID %s" article))
761 (cons 'criteria "")
762 (cons 'shortcut t)))
763 (unless (and nnir-artlist (equal query nnir-memo-query)
764 (equal server nnir-memo-server))
765 (setq nnir-artlist (nnir-run-imap query server)
766 nnir-memo-query query
767 nnir-memo-server server))
768 (setq article 1))
769 (unless (zerop (nnir-artlist-length nnir-artlist))
770 (let ((artfullgroup (nnir-article-group article))
771 (artno (nnir-article-number article)))
772 (message "Requesting article %d from group %s"
773 artno artfullgroup)
774 (if to-buffer
775 (with-current-buffer to-buffer
776 (let ((gnus-article-decode-hook nil))
777 (gnus-request-article-this-buffer artno artfullgroup)))
778 (gnus-request-article artno artfullgroup))
779 (cons artfullgroup artno)))))))
780
781(deffoo nnir-request-move-article (article group server accept-form
782 &optional last _internal-move-group)
783 (nnir-possibly-change-group group server)
784 (let* ((artfullgroup (nnir-article-group article))
785 (artno (nnir-article-number article))
786 (to-newsgroup (nth 1 accept-form))
787 (to-method (gnus-find-method-for-group to-newsgroup))
788 (from-method (gnus-find-method-for-group artfullgroup))
789 (move-is-internal (gnus-server-equal from-method to-method)))
790 (unless (gnus-check-backend-function
791 'request-move-article artfullgroup)
792 (error "The group %s does not support article moving" artfullgroup))
793 (gnus-request-move-article
794 artno
795 artfullgroup
796 (nth 1 from-method)
797 accept-form
798 last
799 (and move-is-internal
800 to-newsgroup ; Not respooling
801 (gnus-group-real-name to-newsgroup)))))
802
803(deffoo nnir-request-expire-articles (articles group &optional server force)
804 (nnir-possibly-change-group group server)
805 (if force
806 (let ((articles-by-group (nnir-categorize
807 articles nnir-article-group nnir-article-ids))
808 not-deleted)
809 (while (not (null articles-by-group))
810 (let* ((group-articles (pop articles-by-group))
811 (artgroup (car group-articles))
812 (articleids (cadr group-articles))
813 (artlist (sort (mapcar 'cdr articleids) '<)))
814 (unless (gnus-check-backend-function 'request-expire-articles
815 artgroup)
816 (error "The group %s does not support article deletion" artgroup))
817 (unless (gnus-check-server (gnus-find-method-for-group artgroup))
818 (error "Couldn't open server for group %s" artgroup))
819 (push (gnus-request-expire-articles
820 artlist artgroup force)
821 not-deleted)))
822 (sort (delq nil not-deleted) '<))
823 articles))
824
825(deffoo nnir-warp-to-article ()
826 (nnir-possibly-change-group gnus-newsgroup-name)
827 (let* ((cur (if (> (gnus-summary-article-number) 0)
828 (gnus-summary-article-number)
829 (error "Can't warp to a pseudo-article")))
830 (backend-article-group (nnir-article-group cur))
831 (backend-article-number (nnir-article-number cur))
832; (quit-config (gnus-ephemeral-group-p gnus-newsgroup-name))
833 )
834
835 ;; what should we do here? we could leave all the buffers around
836 ;; and assume that we have to exit from them one by one. or we can
837 ;; try to clean up directly
838
839 ;;first exit from the nnir summary buffer.
840; (gnus-summary-exit)
841 ;; and if the nnir summary buffer in turn came from another
842 ;; summary buffer we have to clean that summary up too.
843 ; (when (not (eq (cdr quit-config) 'group))
844; (gnus-summary-exit))
845 (gnus-summary-read-group-1 backend-article-group t t nil
846 nil (list backend-article-number))))
847
848(deffoo nnir-request-update-mark (_group article mark)
849 (let ((artgroup (nnir-article-group article))
850 (artnumber (nnir-article-number article)))
851 (or (and artgroup
852 artnumber
853 (gnus-request-update-mark artgroup artnumber mark))
854 mark)))
855
856(deffoo nnir-request-set-mark (group actions &optional server)
857 (nnir-possibly-change-group group server)
858 (let (mlist)
859 (dolist (action actions)
860 (cl-destructuring-bind (range action marks) action
861 (let ((articles-by-group (nnir-categorize
862 (gnus-uncompress-range range)
863 nnir-article-group nnir-article-number)))
864 (dolist (artgroup articles-by-group)
865 (push (list
866 (car artgroup)
867 (list (gnus-compress-sequence
868 (sort (cadr artgroup) '<))
869 action marks))
870 mlist)))))
871 (dolist (request (nnir-categorize mlist car cadr))
872 (gnus-request-set-mark (car request) (cadr request)))))
873
874
875(deffoo nnir-request-update-info (group info &optional server)
876 (nnir-possibly-change-group group server)
877 ;; clear out all existing marks.
878 (setf (gnus-info-marks info) nil)
879 (setf (gnus-info-read info) nil)
880 (let ((group (gnus-group-guess-full-name-from-command-method group))
881 (articles-by-group
882 (nnir-categorize
883 (gnus-uncompress-range (cons 1 (nnir-artlist-length nnir-artlist)))
884 nnir-article-group nnir-article-ids)))
885 (gnus-set-active group
886 (cons 1 (nnir-artlist-length nnir-artlist)))
887 (while (not (null articles-by-group))
888 (let* ((group-articles (pop articles-by-group))
889 (articleids (reverse (cadr group-articles)))
890 (group-info (gnus-get-info (car group-articles)))
891 (marks (gnus-info-marks group-info))
892 (read (gnus-info-read group-info)))
893 (setf (gnus-info-read info)
894 (gnus-add-to-range
895 (gnus-info-read info)
896 (delq nil
897 (mapcar
898 #'(lambda (art)
899 (when (gnus-member-of-range (cdr art) read)
900 (car art)))
901 articleids))))
902 (dolist (mark marks)
903 (cl-destructuring-bind (type . range) mark
904 (gnus-add-marked-articles
905 group type
906 (delq nil
907 (mapcar
908 #'(lambda (art)
909 (when (gnus-member-of-range (cdr art) range) (car art)))
910 articleids)))))))))
911
912
913(deffoo nnir-close-group (group &optional server)
914 (nnir-possibly-change-group group server)
915 (let ((pgroup (gnus-group-guess-full-name-from-command-method group)))
916 (when (and nnir-artlist (not (gnus-ephemeral-group-p pgroup)))
917 (gnus-group-set-parameter pgroup 'nnir-artlist nnir-artlist))
918 (setq nnir-artlist nil)
919 (when (gnus-ephemeral-group-p pgroup)
920 (gnus-kill-ephemeral-group pgroup)
921 (setq gnus-ephemeral-servers
922 (delq (assq 'nnir gnus-ephemeral-servers)
923 gnus-ephemeral-servers)))))
924;; (gnus-opened-servers-remove
925;; (car (assoc '(nnir "nnir-ephemeral" (nnir-address "nnir"))
926;; gnus-opened-servers))))
927
928
929
930 486
931(defmacro nnir-add-result (dirnam artno score prefix server artlist) 487(defmacro nnir-add-result (dirnam artno score prefix server artlist)
932 "Ask `nnir-compose-result' to construct a result vector, 488 "Construct a result vector and add it to ARTLIST.
933and if it is non-nil, add it to ARTLIST." 489DIRNAM, ARTNO, SCORE, PREFIX and SERVER are passed to
490`nnir-compose-result' to make the vector. Only add the result if
491non-nil."
934 `(let ((result (nnir-compose-result ,dirnam ,artno ,score ,prefix ,server))) 492 `(let ((result (nnir-compose-result ,dirnam ,artno ,score ,prefix ,server)))
935 (when (not (null result)) 493 (when (not (null result))
936 (push result ,artlist)))) 494 (push result ,artlist))))
@@ -940,9 +498,9 @@ and if it is non-nil, add it to ARTLIST."
940;; Helper function currently used by the Swish++ and Namazu backends; 498;; Helper function currently used by the Swish++ and Namazu backends;
941;; perhaps useful for other backends as well 499;; perhaps useful for other backends as well
942(defun nnir-compose-result (dirnam article score prefix server) 500(defun nnir-compose-result (dirnam article score prefix server)
943 "Extract the group from DIRNAM, and create a result vector 501 "Construct a result vector.
944ready to be added to the list of search results." 502The DIRNAM, ARTICLE, SCORE, PREFIX, and SERVER are used to
945 503construct the vector entries."
946 ;; remove nnir-*-remove-prefix from beginning of dirnam filename 504 ;; remove nnir-*-remove-prefix from beginning of dirnam filename
947 (when (string-match (concat "^" prefix) dirnam) 505 (when (string-match (concat "^" prefix) dirnam)
948 (setq dirnam (replace-match "" t t dirnam))) 506 (setq dirnam (replace-match "" t t dirnam)))
@@ -977,13 +535,14 @@ ready to be added to the list of search results."
977 535
978;; imap interface 536;; imap interface
979(defun nnir-run-imap (query srv &optional groups) 537(defun nnir-run-imap (query srv &optional groups)
980 "Run a search against an IMAP back-end server. 538 "Run the QUERY search against an IMAP back-end server SRV.
981This uses a custom query language parser; see `nnir-imap-make-query' 539Search GROUPS, or all active groups on SRV if GROUPS is nil.
982for details on the language and supported extensions." 540This uses a custom query language parser; see
541`nnir-imap-make-query' for details on the language and supported
542extensions."
983 (save-excursion 543 (save-excursion
984 (let ((qstring (cdr (assq 'query query))) 544 (let ((qstring (cdr (assq 'query query)))
985 (server (cadr (gnus-server-to-method srv))) 545 (server (cadr (gnus-server-to-method srv)))
986;; (defs (nth 2 (gnus-server-to-method srv)))
987 (criteria (or (cdr (assq 'criteria query)) 546 (criteria (or (cdr (assq 'criteria query))
988 (cdr (assoc nnir-imap-default-search-key 547 (cdr (assoc nnir-imap-default-search-key
989 nnir-imap-search-arguments)))) 548 nnir-imap-search-arguments))))
@@ -995,38 +554,37 @@ for details on the language and supported extensions."
995 (catch 'found 554 (catch 'found
996 (mapcar 555 (mapcar
997 #'(lambda (group) 556 #'(lambda (group)
998 (let (artlist) 557 (let (artlist)
999 (condition-case () 558 (condition-case ()
1000 (when (nnimap-change-group 559 (when (nnimap-change-group
1001 (gnus-group-short-name group) server) 560 (gnus-group-short-name group) server)
1002 (with-current-buffer (nnimap-buffer) 561 (with-current-buffer (nnimap-buffer)
1003 (message "Searching %s..." group) 562 (message "Searching %s..." group)
1004 (let ((arts 0) 563 (let ((arts 0)
1005 (result (nnimap-command "UID SEARCH %s" 564 (result (nnimap-command "UID SEARCH %s"
1006 (if (string= criteria "") 565 (if (string= criteria "")
1007 qstring 566 qstring
1008 (nnir-imap-make-query 567 (nnir-imap-make-query
1009 criteria qstring))))) 568 criteria qstring)))))
1010 (mapc 569 (mapc
1011 (lambda (artnum) 570 (lambda (artnum)
1012 (let ((artn (string-to-number artnum))) 571 (let ((artn (string-to-number artnum)))
1013 (when (> artn 0) 572 (when (> artn 0)
1014 (push (vector group artn 100) 573 (push (vector group artn 100)
1015 artlist) 574 artlist)
1016 (when (assq 'shortcut query) 575 (when (assq 'shortcut query)
1017 (throw 'found (list artlist))) 576 (throw 'found (list artlist)))
1018 (setq arts (1+ arts))))) 577 (setq arts (1+ arts)))))
1019 (and (car result) 578 (and (car result)
1020 (cdr (assoc "SEARCH" (cdr result))))) 579 (cdr (assoc "SEARCH" (cdr result)))))
1021 (message "Searching %s... %d matches" group arts))) 580 (message "Searching %s... %d matches" group arts)))
1022 (message "Searching %s...done" group)) 581 (message "Searching %s...done" group))
1023 (quit nil)) 582 (quit nil))
1024 (nreverse artlist))) 583 (nreverse artlist)))
1025 groups)))))) 584 groups))))))
1026 585
1027(defun nnir-imap-make-query (criteria qstring) 586(defun nnir-imap-make-query (criteria qstring)
1028 "Parse the query string and criteria into an appropriate IMAP search 587 "Make an IMAP search expression from QSTRING and CRITERIA.
1029expression, returning the string query to make.
1030 588
1031This implements a little language designed to return the expected 589This implements a little language designed to return the expected
1032results to an arbitrary query string to the end user. 590results to an arbitrary query string to the end user.
@@ -1063,7 +621,7 @@ In the future the following will be added to the language:
1063 621
1064 622
1065(defun nnir-imap-query-to-imap (criteria query) 623(defun nnir-imap-query-to-imap (criteria query)
1066 "Turn an s-expression format QUERY into IMAP." 624 "Turn an s-expression format QUERY with CRITERIA into IMAP."
1067 (mapconcat 625 (mapconcat
1068 ;; Turn the expressions into IMAP text 626 ;; Turn the expressions into IMAP text
1069 (lambda (item) 627 (lambda (item)
@@ -1099,8 +657,9 @@ In the future the following will be added to the language:
1099 657
1100 658
1101(defun nnir-imap-parse-query (string) 659(defun nnir-imap-parse-query (string)
1102 "Turn STRING into an s-expression based query based on the IMAP 660 "Turn STRING into an s-expression query.
1103query language as defined in `nnir-imap-make-query'. 661STRING is based on the IMAP query language as defined in
662`nnir-imap-make-query'.
1104 663
1105This involves turning individual tokens into higher level terms 664This involves turning individual tokens into higher level terms
1106that the search language can then understand and use." 665that the search language can then understand and use."
@@ -1116,7 +675,7 @@ that the search language can then understand and use."
1116 675
1117 676
1118(defun nnir-imap-next-expr (&optional count) 677(defun nnir-imap-next-expr (&optional count)
1119 "Return the next expression from the current buffer." 678 "Return the next (COUNT) expression from the current buffer."
1120 (let ((term (nnir-imap-next-term count)) 679 (let ((term (nnir-imap-next-term count))
1121 (next (nnir-imap-peek-symbol))) 680 (next (nnir-imap-peek-symbol)))
1122 ;; Are we looking at an 'or' expression? 681 ;; Are we looking at an 'or' expression?
@@ -1129,7 +688,7 @@ that the search language can then understand and use."
1129 688
1130 689
1131(defun nnir-imap-next-term (&optional count) 690(defun nnir-imap-next-term (&optional count)
1132 "Return the next term from the current buffer." 691 "Return the next (COUNT) term from the current buffer."
1133 (let ((term (nnir-imap-next-symbol count))) 692 (let ((term (nnir-imap-next-symbol count)))
1134 ;; What sort of term is this? 693 ;; What sort of term is this?
1135 (cond 694 (cond
@@ -1147,9 +706,10 @@ that the search language can then understand and use."
1147 (nnir-imap-next-symbol))) 706 (nnir-imap-next-symbol)))
1148 707
1149(defun nnir-imap-next-symbol (&optional count) 708(defun nnir-imap-next-symbol (&optional count)
1150 "Return the next symbol from the current buffer, or nil if we are 709 "Return the next (COUNT) symbol from the current buffer.
1151at the end of the buffer. If supplied COUNT skips some symbols before 710Return nil if we are at the end of the buffer. If supplied COUNT
1152returning the one at the supplied position." 711skips some symbols before returning the one at the supplied
712position."
1153 (when (and (numberp count) (> count 1)) 713 (when (and (numberp count) (> count 1))
1154 (nnir-imap-next-symbol (1- count))) 714 (nnir-imap-next-symbol (1- count)))
1155 (let ((case-fold-search t)) 715 (let ((case-fold-search t))
@@ -1180,7 +740,7 @@ returning the one at the supplied position."
1180 (buffer-substring start end))))))) 740 (buffer-substring start end)))))))
1181 741
1182(defun nnir-imap-delimited-string (delimiter) 742(defun nnir-imap-delimited-string (delimiter)
1183 "Return a delimited string from the current buffer." 743 "Return a string delimited by DELIMITER from the current buffer."
1184 (let ((start (point)) end) 744 (let ((start (point)) end)
1185 (forward-char 1) ; skip the first delimiter. 745 (forward-char 1) ; skip the first delimiter.
1186 (while (not end) 746 (while (not end)
@@ -1207,7 +767,7 @@ returning the one at the supplied position."
1207;; - file size 767;; - file size
1208;; - group 768;; - group
1209(defun nnir-run-swish++ (query server &optional _group) 769(defun nnir-run-swish++ (query server &optional _group)
1210 "Run QUERY against swish++. 770 "Run QUERY on SERVER against swish++.
1211Returns a vector of (group name, file name) pairs (also vectors, 771Returns a vector of (group name, file name) pairs (also vectors,
1212actually). 772actually).
1213 773
@@ -1297,7 +857,7 @@ Windows NT 4.0."
1297 857
1298;; Swish-E interface. 858;; Swish-E interface.
1299(defun nnir-run-swish-e (query server &optional _group) 859(defun nnir-run-swish-e (query server &optional _group)
1300 "Run given QUERY against swish-e. 860 "Run given QUERY on SERVER against swish-e.
1301Returns a vector of (group name, file name) pairs (also vectors, 861Returns a vector of (group name, file name) pairs (also vectors,
1302actually). 862actually).
1303 863
@@ -1392,6 +952,7 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
1392 952
1393;; HyREX interface 953;; HyREX interface
1394(defun nnir-run-hyrex (query server &optional group) 954(defun nnir-run-hyrex (query server &optional group)
955 "Run given QUERY with GROUP on SERVER against hyrex."
1395 (save-excursion 956 (save-excursion
1396 (let ((artlist nil) 957 (let ((artlist nil)
1397 (groupspec (cdr (assq 'hyrex-group query))) 958 (groupspec (cdr (assq 'hyrex-group query)))
@@ -1463,7 +1024,7 @@ Tested with swish-e-2.0.1 on Windows NT 4.0."
1463 1024
1464;; Namazu interface 1025;; Namazu interface
1465(defun nnir-run-namazu (query server &optional _group) 1026(defun nnir-run-namazu (query server &optional _group)
1466 "Run given QUERY against Namazu. 1027 "Run QUERY on SERVER against Namazu.
1467Returns a vector of (group name, file name) pairs (also vectors, 1028Returns a vector of (group name, file name) pairs (also vectors,
1468actually). 1029actually).
1469 1030
@@ -1533,7 +1094,7 @@ Tested with Namazu 2.0.6 on a GNU/Linux system."
1533 (nnir-artitem-rsv y))))))))) 1094 (nnir-artitem-rsv y)))))))))
1534 1095
1535(defun nnir-run-notmuch (query server &optional groups) 1096(defun nnir-run-notmuch (query server &optional groups)
1536 "Run QUERY against notmuch. 1097 "Run QUERY with GROUPS from SERVER against notmuch.
1537Returns a vector of (group name, file name) pairs (also vectors, 1098Returns a vector of (group name, file name) pairs (also vectors,
1538actually). If GROUPS is a list of group names, use them to 1099actually). If GROUPS is a list of group names, use them to
1539construct path: search terms (see the variable 1100construct path: search terms (see the variable
@@ -1617,7 +1178,7 @@ construct path: search terms (see the variable
1617 artlist))) 1178 artlist)))
1618 1179
1619(defun nnir-run-find-grep (query server &optional grouplist) 1180(defun nnir-run-find-grep (query server &optional grouplist)
1620 "Run find and grep to obtain matching articles." 1181 "Run find and grep to QUERY GROUPLIST on SERVER for matching articles."
1621 (let* ((method (gnus-server-to-method server)) 1182 (let* ((method (gnus-server-to-method server))
1622 (sym (intern 1183 (sym (intern
1623 (concat (symbol-name (car method)) "-directory"))) 1184 (concat (symbol-name (car method)) "-directory")))
@@ -1703,14 +1264,10 @@ construct path: search terms (see the variable
1703 1264
1704;;; Util Code: 1265;;; Util Code:
1705 1266
1706(defun gnus-nnir-group-p (group)
1707 "Say whether GROUP is nnir or not."
1708 (if (gnus-group-prefixed-p group)
1709 (eq 'nnir (car (gnus-find-method-for-group group)))
1710 (and group (string-match "^nnir" group))))
1711 1267
1712(defun nnir-read-parms (nnir-search-engine) 1268(defun nnir-read-parms (nnir-search-engine)
1713 "Read additional search parameters according to `nnir-engines'." 1269 "Read additional search parameters for NNIR-SEARCH-ENGINE.
1270Parameters are according to `nnir-engines'."
1714 (let ((parmspec (nth 2 (assoc nnir-search-engine nnir-engines)))) 1271 (let ((parmspec (nth 2 (assoc nnir-search-engine nnir-engines))))
1715 (mapcar #'nnir-read-parm parmspec))) 1272 (mapcar #'nnir-read-parm parmspec)))
1716 1273
@@ -1727,7 +1284,7 @@ PARMSPEC is a cons cell, the car is a symbol, the cdr is a prompt."
1727 (cons sym (read-string prompt))))) 1284 (cons sym (read-string prompt)))))
1728 1285
1729(defun nnir-run-query (specs) 1286(defun nnir-run-query (specs)
1730 "Invoke appropriate search engine function (see `nnir-engines')." 1287 "Invoke search engine appropriate for SPECS (see `nnir-engines')."
1731 (apply #'vconcat 1288 (apply #'vconcat
1732 (mapcar 1289 (mapcar
1733 (lambda (x) 1290 (lambda (x)
@@ -1736,10 +1293,11 @@ PARMSPEC is a cons cell, the car is a symbol, the cdr is a prompt."
1736 (search-func (cadr (assoc search-engine nnir-engines)))) 1293 (search-func (cadr (assoc search-engine nnir-engines))))
1737 (and search-func 1294 (and search-func
1738 (funcall search-func (cdr (assq 'nnir-query-spec specs)) 1295 (funcall search-func (cdr (assq 'nnir-query-spec specs))
1739 server (cadr x))))) 1296 server (cdr x)))))
1740 (cdr (assq 'nnir-group-spec specs))))) 1297 (cdr (assq 'nnir-group-spec specs)))))
1741 1298
1742(defun nnir-server-to-search-engine (server) 1299(defun nnir-server-to-search-engine (server)
1300 "Find search engine for SERVER."
1743 (or (nnir-read-server-parm 'nnir-search-engine server t) 1301 (or (nnir-read-server-parm 'nnir-search-engine server t)
1744 (cdr (assoc (car (gnus-server-to-method server)) 1302 (cdr (assoc (car (gnus-server-to-method server))
1745 nnir-method-default-engines)))) 1303 nnir-method-default-engines))))
@@ -1754,48 +1312,10 @@ environment unless NOT-GLOBAL is non-nil."
1754 ((and (not not-global) (boundp key)) (symbol-value key)) 1312 ((and (not not-global) (boundp key)) (symbol-value key))
1755 (t nil)))) 1313 (t nil))))
1756 1314
1757(defun nnir-possibly-change-group (group &optional server) 1315(autoload 'gnus-request-list "gnus-int")
1758 (or (not server) (nnir-server-opened server) (nnir-open-server server))
1759 (when (gnus-nnir-group-p group)
1760 (setq nnir-artlist (gnus-group-get-parameter
1761 (gnus-group-prefixed-name
1762 (gnus-group-short-name group) '(nnir "nnir"))
1763 'nnir-artlist t))))
1764
1765(defun nnir-server-opened (&optional server)
1766 (let ((backend (car (gnus-server-to-method server))))
1767 (nnoo-current-server-p (or backend 'nnir) server)))
1768
1769(autoload 'nnimap-make-thread-query "nnimap")
1770(declare-function gnus-registry-get-id-key "gnus-registry" (id key))
1771
1772(defun nnir-search-thread (header)
1773 "Make an nnir group based on the thread containing the article HEADER.
1774The current server will be searched. If the registry is installed,
1775the server that the registry reports the current article came from
1776is also searched."
1777 (let* ((query
1778 (list (cons 'query (nnimap-make-thread-query header))
1779 (cons 'criteria "")))
1780 (server
1781 (list (list (gnus-method-to-server
1782 (gnus-find-method-for-group gnus-newsgroup-name)))))
1783 (registry-group (and
1784 (bound-and-true-p gnus-registry-enabled)
1785 (car (gnus-registry-get-id-key
1786 (mail-header-id header) 'group))))
1787 (registry-server
1788 (and registry-group
1789 (gnus-method-to-server
1790 (gnus-find-method-for-group registry-group)))))
1791 (when registry-server
1792 (cl-pushnew (list registry-server) server :test #'equal))
1793 (gnus-group-make-nnir-group nil (list
1794 (cons 'nnir-query-spec query)
1795 (cons 'nnir-group-spec server)))
1796 (gnus-summary-goto-subject (gnus-id-to-article (mail-header-id header)))))
1797 1316
1798(defun nnir-get-active (srv) 1317(defun nnir-get-active (srv)
1318 "Return the active list for SRV."
1799 (let ((method (gnus-server-to-method srv)) 1319 (let ((method (gnus-server-to-method srv))
1800 groups) 1320 groups)
1801 (gnus-request-list method) 1321 (gnus-request-list method)
@@ -1835,82 +1355,37 @@ is also searched."
1835 (forward-line))))) 1355 (forward-line)))))
1836 groups)) 1356 groups))
1837 1357
1838;; Behind gnus-registry-enabled test. 1358(autoload 'nnselect-categorize "nnselect" nil nil)
1839(declare-function gnus-registry-action "gnus-registry" 1359(autoload 'gnus-group-topic-name "gnus-topic" nil nil)
1840 (action data-header from &optional to method)) 1360(defvar gnus-group-marked)
1841 1361(defvar gnus-topic-alist)
1842(defun nnir-registry-action (action data-header _from &optional to method) 1362
1843 "Call `gnus-registry-action' with the original article group." 1363(defun nnir-make-specs (nnir-extra-parms &optional specs)
1844 (gnus-registry-action 1364 "Make the query-spec and group-spec for a search with NNIR-EXTRA-PARMS.
1845 action 1365Query for the specs, or use SPECS."
1846 data-header 1366 (let* ((group-spec
1847 (nnir-article-group (mail-header-number data-header)) 1367 (or (cdr (assq 'nnir-group-spec specs))
1848 to 1368 (if (gnus-server-server-name)
1849 method)) 1369 (list (list (gnus-server-server-name)))
1850 1370 (nnselect-categorize
1851(defun nnir-mode () 1371 (or gnus-group-marked
1852 (when (eq (car (gnus-find-method-for-group gnus-newsgroup-name)) 'nnir) 1372 (if (gnus-group-group-name)
1853 (when (and nnir-summary-line-format 1373 (list (gnus-group-group-name))
1854 (not (string= nnir-summary-line-format 1374 (cdr (assoc (gnus-group-topic-name) gnus-topic-alist))))
1855 gnus-summary-line-format))) 1375 'nnselect-group-server))))
1856 (setq gnus-summary-line-format nnir-summary-line-format) 1376 (query-spec
1857 (gnus-update-format-specifications nil 'summary)) 1377 (or (cdr (assq 'nnir-query-spec specs))
1858 (when (bound-and-true-p gnus-registry-enabled) 1378 (apply
1859 (remove-hook 'gnus-summary-article-delete-hook 'gnus-registry-action t) 1379 'append
1860 (remove-hook 'gnus-summary-article-move-hook 'gnus-registry-action t) 1380 (list (cons 'query
1861 (remove-hook 'gnus-summary-article-expire-hook 'gnus-registry-action t) 1381 (read-string "Query: " nil 'nnir-search-history)))
1862 (add-hook 'gnus-summary-article-delete-hook 'nnir-registry-action t t) 1382 (when nnir-extra-parms
1863 (add-hook 'gnus-summary-article-move-hook 'nnir-registry-action t t) 1383 (mapcar
1864 (add-hook 'gnus-summary-article-expire-hook 'nnir-registry-action t t)))) 1384 (lambda (x)
1865 1385 (nnir-read-parms (nnir-server-to-search-engine (car x))))
1866 1386 group-spec))))))
1867(defun gnus-summary-create-nnir-group () 1387 (list (cons 'nnir-query-spec query-spec)
1868 (interactive) 1388 (cons 'nnir-group-spec group-spec))))
1869 (or (nnir-server-opened "") (nnir-open-server "nnir"))
1870 (let ((name (gnus-read-group "Group name: "))
1871 (method '(nnir ""))
1872 (pgroup
1873 (gnus-group-guess-full-name-from-command-method gnus-newsgroup-name)))
1874 (with-current-buffer gnus-group-buffer
1875 (gnus-group-make-group
1876 name method nil
1877 (gnus-group-find-parameter pgroup)))))
1878
1879
1880(deffoo nnir-request-create-group (group &optional _server args)
1881 (message "Creating nnir group %s" group)
1882 (let* ((group (gnus-group-prefixed-name group '(nnir "nnir")))
1883 (specs (assq 'nnir-specs args))
1884 (query-spec
1885 (or (cdr (assq 'nnir-query-spec specs))
1886 (list (cons 'query
1887 (read-string "Query: " nil 'nnir-search-history)))))
1888 (group-spec
1889 (or (cdr (assq 'nnir-group-spec specs))
1890 (list (list (read-string "Server: " nil nil)))))
1891 (nnir-specs (list (cons 'nnir-query-spec query-spec)
1892 (cons 'nnir-group-spec group-spec))))
1893 (gnus-group-set-parameter group 'nnir-specs nnir-specs)
1894 (gnus-group-set-parameter
1895 group 'nnir-artlist
1896 (or (cdr (assq 'nnir-artlist args))
1897 (nnir-run-query nnir-specs)))
1898 (nnir-request-update-info group (gnus-get-info group)))
1899 t)
1900
1901(deffoo nnir-request-delete-group (_group &optional _force _server)
1902 t)
1903
1904(deffoo nnir-request-list (&optional _server)
1905 t)
1906
1907(deffoo nnir-request-scan (_group _method)
1908 t)
1909
1910(deffoo nnir-request-close ()
1911 t)
1912
1913(nnoo-define-skeleton nnir)
1914 1389
1915;; The end. 1390;; The end.
1916(provide 'nnir) 1391(provide 'nnir)
diff --git a/lisp/gnus/nnmaildir.el b/lisp/gnus/nnmaildir.el
index 9c7b1254413..81a148db669 100644
--- a/lisp/gnus/nnmaildir.el
+++ b/lisp/gnus/nnmaildir.el
@@ -492,7 +492,7 @@ This variable is set by `nnmaildir-request-article'.")
492 (setq nov-mid 0)) 492 (setq nov-mid 0))
493 (goto-char (point-min)) 493 (goto-char (point-min))
494 (delete-char 1) 494 (delete-char 1)
495 (setq nov (nnheader-parse-naked-head) 495 (setq nov (nnheader-parse-head t)
496 field (or (mail-header-lines nov) 0))) 496 field (or (mail-header-lines nov) 0)))
497 (unless (or (zerop field) (nnmaildir--param pgname 'distrust-Lines:)) 497 (unless (or (zerop field) (nnmaildir--param pgname 'distrust-Lines:))
498 (setq nov-mid field)) 498 (setq nov-mid field))
diff --git a/lisp/gnus/nnml.el b/lisp/gnus/nnml.el
index baf5d54b74d..ad608b6575e 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."
766 (if (re-search-forward "\n\r?\n" nil t) 766 (if (re-search-forward "\n\r?\n" nil t)
767 (1- (point)) 767 (1- (point))
768 (point-max)))) 768 (point-max))))
769 (let ((headers (nnheader-parse-naked-head))) 769 (let ((headers (nnheader-parse-head t)))
770 (setf (mail-header-chars headers) chars) 770 (setf (mail-header-chars headers) chars)
771 (setf (mail-header-number headers) number) 771 (setf (mail-header-number headers) number)
772 headers)))) 772 headers))))
diff --git a/lisp/gnus/nnselect.el b/lisp/gnus/nnselect.el
new file mode 100644
index 00000000000..460bc63132c
--- /dev/null
+++ b/lisp/gnus/nnselect.el
@@ -0,0 +1,864 @@
1;;; nnselect.el --- a virtual group backend -*- lexical-binding:t -*-
2
3;; Copyright (C) 2020 Free Software Foundation, Inc.
4
5;; Author: Andrew Cohen <cohen@andy.bu.edu>
6;; Keywords: news mail
7
8;; This file is part of GNU Emacs.
9
10;; GNU Emacs is free software: you can redistribute it and/or modify
11;; it under the terms of the GNU General Public License as published by
12;; the Free Software Foundation, either version 3 of the License, or
13;; (at your option) any later version.
14
15;; GNU Emacs is distributed in the hope that it will be useful,
16;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18;; GNU General Public License for more details.
19
20;; You should have received a copy of the GNU General Public License
21;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22
23;;; Commentary:
24
25;; This is a "virtual" backend that allows an aribtrary list of
26;; articles to be treated as a gnus group. An nnselect group uses an
27;; nnselect-spec group parameter to specify this list of
28;; articles. nnselect-spec is an alist with two keys:
29;; nnselect-function, whose value should be a function that returns
30;; the list of articles, and nnselect-args. The function will be
31;; applied to the arguments to generate the list of articles. The
32;; return value should be a vector, each element of which should in
33;; turn be a vector of three elements: a real prefixed group name, an
34;; article number in that group, and an integer score. The score is
35;; not used by nnselect but may be used by other code to help in
36;; sorting. Most functions will just chose a fixed number, such as
37;; 100, for this score.
38
39;; For example the search function `nnir-run-query' applied to
40;; arguments specifying a search query (see "nnir.el") can be used to
41;; return a list of articles from a search. Or the function can be the
42;; identity and the args a vector of articles.
43
44
45;;; Code:
46
47;;; Setup:
48
49(require 'gnus-art)
50(require 'nnir)
51
52(eval-when-compile (require 'cl-lib))
53
54;; Set up the backend
55
56(nnoo-declare nnselect)
57
58(nnoo-define-basics nnselect)
59
60(gnus-declare-backend "nnselect" 'post-mail 'virtual)
61
62;;; Internal Variables:
63
64(defvar gnus-inhibit-demon)
65(defvar gnus-message-group-art)
66
67;; For future use
68(defvoo nnselect-directory gnus-directory
69 "Directory for the nnselect backend.")
70
71(defvoo nnselect-active-file
72 (expand-file-name "nnselect-active" nnselect-directory)
73 "nnselect active file.")
74
75(defvoo nnselect-groups-file
76 (expand-file-name "nnselect-newsgroups" nnselect-directory)
77 "nnselect groups description file.")
78
79;;; Helper routines.
80(defun nnselect-compress-artlist (artlist)
81 "Compress ARTLIST."
82 (let (selection)
83 (pcase-dolist (`(,artgroup . ,arts)
84 (nnselect-categorize artlist 'nnselect-artitem-group))
85 (let (list)
86 (pcase-dolist (`(,rsv . ,articles)
87 (nnselect-categorize
88 arts 'nnselect-artitem-rsv 'nnselect-artitem-number))
89 (push (cons rsv (gnus-compress-sequence (sort articles '<)))
90 list))
91 (push (cons artgroup list) selection)))
92 selection))
93
94(defun nnselect-uncompress-artlist (artlist)
95 "Uncompress ARTLIST."
96 (if (vectorp artlist)
97 artlist
98 (let (selection)
99 (pcase-dolist (`(,artgroup (,artrsv . ,artseq)) artlist)
100 (setq selection
101 (vconcat
102 (cl-map 'vector
103 #'(lambda (art)
104 (vector artgroup art artrsv))
105 (gnus-uncompress-sequence artseq)) selection)))
106 selection)))
107
108(defun nnselect-group-server (group)
109 "Return the server for GROUP."
110 (gnus-group-server group))
111
112;; Data type article list.
113
114(define-inline nnselect-artlist-length (artlist)
115 (inline-quote (length ,artlist)))
116
117(define-inline nnselect-artlist-article (artlist n)
118 "Return from ARTLIST the Nth artitem (counting starting at 1)."
119 (inline-quote (when (> ,n 0)
120 (elt ,artlist (1- ,n)))))
121
122(define-inline nnselect-artitem-group (artitem)
123 "Return the group from the ARTITEM."
124 (inline-quote (elt ,artitem 0)))
125
126(define-inline nnselect-artitem-number (artitem)
127 "Return the number from the ARTITEM."
128 (inline-quote (elt ,artitem 1)))
129
130(define-inline nnselect-artitem-rsv (artitem)
131 "Return the Retrieval Status Value (RSV, score) from the ARTITEM."
132 (inline-quote (elt ,artitem 2)))
133
134(define-inline nnselect-article-group (article)
135 "Return the group for ARTICLE."
136 (inline-quote
137 (nnselect-artitem-group (nnselect-artlist-article
138 gnus-newsgroup-selection ,article))))
139
140(define-inline nnselect-article-number (article)
141 "Return the number for ARTICLE."
142 (inline-quote (nnselect-artitem-number
143 (nnselect-artlist-article
144 gnus-newsgroup-selection ,article))))
145
146(define-inline nnselect-article-rsv (article)
147 "Return the rsv for ARTICLE."
148 (inline-quote (nnselect-artitem-rsv
149 (nnselect-artlist-article
150 gnus-newsgroup-selection ,article))))
151
152(define-inline nnselect-article-id (article)
153 "Return the pair `(nnselect id . real id)' of ARTICLE."
154 (inline-quote (cons ,article (nnselect-article-number ,article))))
155
156(define-inline nnselect-categorize (sequence keyfunc &optional valuefunc)
157 "Sorts a sequence into categories.
158Returns a list of the form
159`((key1 (element11 element12)) (key2 (element21 element22))'.
160The category key for a member of the sequence is obtained
161as `(keyfunc member)' and the corresponding element is just
162`member' (or `(valuefunc member)' if `valuefunc' is non-nil)."
163 (inline-letevals (sequence keyfunc valuefunc)
164 (inline-quote (let ((valuefunc (or ,valuefunc 'identity))
165 result)
166 (unless (null ,sequence)
167 (mapc
168 (lambda (member)
169 (let* ((key (funcall ,keyfunc member))
170 (value (funcall valuefunc member))
171 (kr (assoc key result)))
172 (if kr
173 (push value (cdr kr))
174 (push (list key value) result))))
175 (reverse ,sequence))
176 result)))))
177
178
179;; Unclear whether a macro or an inline function is best.
180;; (defmacro nnselect-categorize (sequence keyfunc &optional valuefunc)
181;; "Sorts a sequence into categories and returns a list of the form
182;; `((key1 (element11 element12)) (key2 (element21 element22))'.
183;; The category key for a member of the sequence is obtained
184;; as `(keyfunc member)' and the corresponding element is just
185;; `member' (or `(valuefunc member)' if `valuefunc' is non-nil)."
186;; (let ((key (make-symbol "key"))
187;; (value (make-symbol "value"))
188;; (result (make-symbol "result"))
189;; (valuefunc (or valuefunc 'identity)))
190;; `(unless (null ,sequence)
191;; (let (,result)
192;; (mapc
193;; (lambda (member)
194;; (let* ((,key (,keyfunc member))
195;; (,value (,valuefunc member))
196;; (kr (assoc ,key ,result)))
197;; (if kr
198;; (push ,value (cdr kr))
199;; (push (list ,key ,value) ,result))))
200;; (reverse ,sequence))
201;; ,result))))
202
203(define-inline ids-by-group (articles)
204 (inline-quote
205 (nnselect-categorize ,articles 'nnselect-article-group
206 'nnselect-article-id)))
207
208(define-inline numbers-by-group (articles)
209 (inline-quote
210 (nnselect-categorize
211 ,articles 'nnselect-article-group 'nnselect-article-number)))
212
213
214(defmacro nnselect-add-prefix (group)
215 "Ensures that the GROUP has an nnselect prefix."
216 `(gnus-group-prefixed-name
217 (gnus-group-short-name ,group) '(nnselect "nnselect")))
218
219(defmacro nnselect-get-artlist (group)
220 "Retrieve the list of articles for GROUP."
221 `(when (gnus-nnselect-group-p ,group)
222 (nnselect-uncompress-artlist
223 (gnus-group-get-parameter ,group 'nnselect-artlist t))))
224
225(defmacro nnselect-add-novitem (novitem)
226 "Add NOVITEM to the list of headers."
227 `(let* ((novitem ,novitem)
228 (artno (and novitem
229 (mail-header-number novitem)))
230 (art (car-safe (rassq artno artids))))
231 (when art
232 (setf (mail-header-number novitem) art)
233 (push novitem headers))))
234
235;;; User Customizable Variables:
236
237(defgroup nnselect nil
238 "Virtual groups in Gnus with arbitrary selection methods."
239 :group 'gnus)
240
241(defcustom nnselect-retrieve-headers-override-function nil
242 "A function that retrieves article headers for ARTICLES from GROUP.
243The retrieved headers should populate the `nntp-server-buffer'.
244Returns either the retrieved header format 'nov or 'headers.
245
246If this variable is nil, or if the provided function returns nil,
247 `gnus-retrieve-headers' will be called instead."
248 :version "24.1" :type '(function) :group 'nnselect)
249
250
251;; Gnus backend interface functions.
252
253(deffoo nnselect-open-server (server &optional definitions)
254 ;; Just set the server variables appropriately.
255 (let ((backend (or (car (gnus-server-to-method server)) 'nnselect)))
256 (nnoo-change-server backend server definitions)))
257
258;; (deffoo nnselect-server-opened (&optional server)
259;; "Is SERVER the current virtual server?"
260;; (if (string-empty-p server)
261;; t
262;; (let ((backend (car (gnus-server-to-method server))))
263;; (nnoo-current-server-p (or backend 'nnselect) server))))
264
265(deffoo nnselect-server-opened (&optional _server)
266 t)
267
268
269(deffoo nnselect-request-group (group &optional _server _dont-check info)
270 (let* ((group (nnselect-add-prefix group))
271 (nnselect-artlist (nnselect-get-artlist group))
272 length)
273 ;; Check for cached select result or run the selection and cache
274 ;; the result.
275 (unless nnselect-artlist
276 (gnus-group-set-parameter
277 group 'nnselect-artlist
278 (nnselect-compress-artlist (setq nnselect-artlist
279 (nnselect-run
280 (gnus-group-get-parameter group 'nnselect-specs t)))))
281 (nnselect-request-update-info
282 group (or info (gnus-get-info group))))
283 (if (zerop (setq length (nnselect-artlist-length nnselect-artlist)))
284 (progn
285 (nnheader-report 'nnselect "Selection produced empty results.")
286 (nnheader-insert ""))
287 (with-current-buffer nntp-server-buffer
288 (nnheader-insert "211 %d %d %d %s\n"
289 length ; total #
290 1 ; first #
291 length ; last #
292 group))) ; group name
293 nnselect-artlist))
294
295
296(deffoo nnselect-retrieve-headers (articles group &optional _server fetch-old)
297 (let ((group (nnselect-add-prefix group)))
298 (with-current-buffer (gnus-summary-buffer-name group)
299 (setq gnus-newsgroup-selection (or gnus-newsgroup-selection
300 (nnselect-get-artlist group)))
301 (let ((gnus-inhibit-demon t)
302 (gartids (ids-by-group articles))
303 headers)
304 (with-current-buffer nntp-server-buffer
305 (pcase-dolist (`(,artgroup . ,artids) gartids)
306 (let ((artlist (sort (mapcar 'cdr artids) '<))
307 (gnus-override-method (gnus-find-method-for-group artgroup))
308 (fetch-old
309 (or
310 (car-safe
311 (gnus-group-find-parameter artgroup
312 'gnus-fetch-old-headers t))
313 fetch-old)))
314 (erase-buffer)
315 (pcase (setq gnus-headers-retrieved-by
316 (or
317 (and
318 nnselect-retrieve-headers-override-function
319 (funcall
320 nnselect-retrieve-headers-override-function
321 artlist artgroup))
322 (gnus-retrieve-headers
323 artlist artgroup fetch-old)))
324 ('nov
325 (goto-char (point-min))
326 (while (not (eobp))
327 (nnselect-add-novitem
328 (nnheader-parse-nov))
329 (forward-line 1)))
330 ('headers
331 (goto-char (point-min))
332 (while (not (eobp))
333 (nnselect-add-novitem
334 (nnheader-parse-head))
335 (forward-line 1)))
336 ((pred listp)
337 (dolist (novitem gnus-headers-retrieved-by)
338 (nnselect-add-novitem novitem)))
339 (_ (error "Unknown header type %s while requesting articles \
340 of group %s" gnus-headers-retrieved-by artgroup)))))
341 (setq headers
342 (sort
343 headers
344 (lambda (x y)
345 (< (mail-header-number x) (mail-header-number y))))))))))
346
347
348(deffoo nnselect-request-article (article &optional _group server to-buffer)
349 (let* ((gnus-override-method nil)
350 servers group-art artlist)
351 (if (numberp article)
352 (with-current-buffer gnus-summary-buffer
353 (unless (zerop (nnselect-artlist-length
354 gnus-newsgroup-selection))
355 (setq group-art (cons (nnselect-article-group article)
356 (nnselect-article-number article)))))
357 ;; message-id: either coming from a referral or a pseudo-article
358 ;; find the servers for a pseudo-article
359 (if (eq 'nnselect (car (gnus-server-to-method server)))
360 (with-current-buffer gnus-summary-buffer
361 (let ((thread (gnus-id-to-thread article)))
362 (when thread
363 (mapc
364 #'(lambda (x)
365 (when (and x (> x 0))
366 (cl-pushnew
367 (list
368 (gnus-method-to-server
369 (gnus-find-method-for-group
370 (nnselect-article-group x)))) servers :test 'equal)))
371 (gnus-articles-in-thread thread)))))
372 (setq servers (list (list server))))
373 (setq artlist
374 (nnir-run-query
375 (list
376 (cons 'nnir-query-spec
377 (list (cons 'query (format "HEADER Message-ID %s" article))
378 (cons 'criteria "") (cons 'shortcut t)))
379 (cons 'nnir-group-spec servers))))
380 (unless (zerop (nnselect-artlist-length artlist))
381 (setq
382 group-art
383 (cons
384 (nnselect-artitem-group (nnselect-artlist-article artlist 1))
385 (nnselect-artitem-number (nnselect-artlist-article artlist 1))))))
386 (when (numberp (cdr group-art))
387 (message "Requesting article %d from group %s"
388 (cdr group-art) (car group-art))
389 (if to-buffer
390 (with-current-buffer to-buffer
391 (let ((gnus-article-decode-hook nil))
392 (gnus-request-article-this-buffer
393 (cdr group-art) (car group-art))))
394 (gnus-request-article (cdr group-art) (car group-art)))
395 group-art)))
396
397
398(deffoo nnselect-request-move-article
399 (article _group _server accept-form &optional last _internal-move-group)
400 (let* ((artgroup (nnselect-article-group article))
401 (artnumber (nnselect-article-number article))
402 (to-newsgroup (nth 1 accept-form))
403 (to-method (gnus-find-method-for-group to-newsgroup))
404 (from-method (gnus-find-method-for-group artgroup))
405 (move-is-internal (gnus-server-equal from-method to-method)))
406 (unless (gnus-check-backend-function
407 'request-move-article artgroup)
408 (error "The group %s does not support article moving" artgroup))
409 (gnus-request-move-article
410 artnumber
411 artgroup
412 (nth 1 from-method)
413 accept-form
414 last
415 (and move-is-internal
416 to-newsgroup ; Not respooling
417 (gnus-group-real-name to-newsgroup)))))
418
419
420(deffoo nnselect-request-expire-articles
421 (articles _group &optional _server force)
422 (if force
423 (let (not-expired)
424 (pcase-dolist (`(,artgroup . ,artids) (ids-by-group articles))
425 (let ((artlist (sort (mapcar 'cdr artids) '<)))
426 (unless (gnus-check-backend-function 'request-expire-articles
427 artgroup)
428 (error "Group %s does not support article expiration" artgroup))
429 (unless (gnus-check-server (gnus-find-method-for-group artgroup))
430 (error "Couldn't open server for group %s" artgroup))
431 (push (mapcar #'(lambda (art)
432 (car (rassq art artids)))
433 (let ((nnimap-expunge 'immediately))
434 (gnus-request-expire-articles
435 artlist artgroup force)))
436 not-expired)))
437 (sort (delq nil not-expired) '<))
438 articles))
439
440
441(deffoo nnselect-warp-to-article ()
442 (let* ((cur (if (> (gnus-summary-article-number) 0)
443 (gnus-summary-article-number)
444 (error "Can't warp to a pseudo-article")))
445 (artgroup (nnselect-article-group cur))
446 (artnumber (nnselect-article-number cur))
447 (_quit-config (gnus-ephemeral-group-p gnus-newsgroup-name)))
448
449 ;; what should we do here? we could leave all the buffers around
450 ;; and assume that we have to exit from them one by one. or we can
451 ;; try to clean up directly
452
453 ;;first exit from the nnselect summary buffer.
454 ;;(gnus-summary-exit)
455 ;; and if the nnselect summary buffer in turn came from another
456 ;; summary buffer we have to clean that summary up too.
457 ;;(when (not (eq (cdr quit-config) 'group))
458 ;; (gnus-summary-exit))
459 (gnus-summary-read-group-1 artgroup t t nil
460 nil (list artnumber))))
461
462
463;; we pass this through to the real group in case it wants to adjust
464;; the mark. We also use this to mark an article expirable iff it is
465;; expirable in the real group.
466(deffoo nnselect-request-update-mark (_group article mark)
467 (let* ((artgroup (nnselect-article-group article))
468 (artnumber (nnselect-article-number article))
469 (gmark (gnus-request-update-mark artgroup artnumber mark)))
470 (when (and artnumber
471 (memq mark gnus-auto-expirable-marks)
472 (= mark gmark)
473 (gnus-group-auto-expirable-p artgroup))
474 (setq gmark gnus-expirable-mark))
475 gmark))
476
477
478(deffoo nnselect-request-set-mark (_group actions &optional _server)
479 (mapc
480 (lambda (request) (gnus-request-set-mark (car request) (cdr request)))
481 (nnselect-categorize
482 (cl-mapcan
483 (lambda (act)
484 (cl-destructuring-bind (range action marks) act
485 (mapcar
486 (lambda (artgroup)
487 (list (car artgroup)
488 (gnus-compress-sequence (sort (cdr artgroup) '<))
489 action marks))
490 (numbers-by-group
491 (gnus-uncompress-range range)))))
492 actions)
493 'car 'cdr)))
494
495(deffoo nnselect-request-update-info (group info &optional _server)
496 (let* ((group (nnselect-add-prefix group))
497 (gnus-newsgroup-selection (or gnus-newsgroup-selection
498 (nnselect-get-artlist group))))
499 (gnus-info-set-marks info nil)
500 (setf (gnus-info-read info) nil)
501 (pcase-dolist (`(,artgroup . ,nartids)
502 (ids-by-group
503 (number-sequence 1 (nnselect-artlist-length
504 gnus-newsgroup-selection))))
505 (let* ((gnus-newsgroup-active nil)
506 (artids (cl-sort nartids '< :key 'car))
507 (group-info (gnus-get-info artgroup))
508 (marks (gnus-info-marks group-info))
509 (unread (gnus-uncompress-sequence
510 (gnus-range-difference (gnus-active artgroup)
511 (gnus-info-read group-info)))))
512 (gnus-atomic-progn
513 (setf (gnus-info-read info)
514 (gnus-add-to-range
515 (gnus-info-read info)
516 (delq nil
517 (mapcar
518 #'(lambda (art)
519 (unless (memq (cdr art) unread) (car art)))
520 artids))))
521 (pcase-dolist (`(,type . ,range) marks)
522 (setq range (gnus-uncompress-sequence range))
523 (gnus-add-marked-articles
524 group type
525 (delq nil
526 (mapcar
527 #'(lambda (art)
528 (when (memq (cdr art) range)
529 (car art))) artids)))))))
530 (gnus-set-active group (cons 1 (nnselect-artlist-length
531 gnus-newsgroup-selection)))))
532
533
534(deffoo nnselect-request-thread (header &optional group server)
535 (with-current-buffer gnus-summary-buffer
536 (let ((group (nnselect-add-prefix group))
537 ;; find the best group for the originating article. if its a
538 ;; pseudo-article look for real articles in the same thread
539 ;; and see where they come from.
540 (artgroup (nnselect-article-group
541 (if (> (mail-header-number header) 0)
542 (mail-header-number header)
543 (if (> (gnus-summary-article-number) 0)
544 (gnus-summary-article-number)
545 (let ((thread
546 (gnus-id-to-thread (mail-header-id header))))
547 (when thread
548 (cl-some #'(lambda (x)
549 (when (and x (> x 0)) x))
550 (gnus-articles-in-thread thread)))))))))
551 ;; Check if we are dealing with an imap backend.
552 (if (eq 'nnimap
553 (car (gnus-find-method-for-group artgroup)))
554 ;; If so we perform the query, massage the result, and return
555 ;; the new headers back to the caller to incorporate into the
556 ;; current summary buffer.
557 (let* ((group-spec
558 (list (delq nil (list
559 (or server (gnus-group-server artgroup))
560 (unless gnus-refer-thread-use-search
561 artgroup)))))
562 (query-spec
563 (list (cons 'query (nnimap-make-thread-query header))
564 (cons 'criteria "")))
565 (last (nnselect-artlist-length gnus-newsgroup-selection))
566 (first (1+ last))
567 (new-nnselect-artlist
568 (nnir-run-query
569 (list (cons 'nnir-query-spec query-spec)
570 (cons 'nnir-group-spec group-spec))))
571 old-arts seq
572 headers)
573 (mapc
574 #'(lambda (article)
575 (if
576 (setq seq
577 (cl-position article
578 gnus-newsgroup-selection :test 'equal))
579 (push (1+ seq) old-arts)
580 (setq gnus-newsgroup-selection
581 (vconcat gnus-newsgroup-selection (vector article)))
582 (cl-incf last)))
583 new-nnselect-artlist)
584 (setq headers
585 (gnus-fetch-headers
586 (append (sort old-arts '<)
587 (number-sequence first last)) nil t))
588 (gnus-group-set-parameter
589 group
590 'nnselect-artlist
591 (nnselect-compress-artlist gnus-newsgroup-selection))
592 (when (>= last first)
593 (let (new-marks)
594 (pcase-dolist (`(,artgroup . ,artids)
595 (ids-by-group (number-sequence first last)))
596 (pcase-dolist (`(,type . ,marked)
597 (gnus-info-marks (gnus-get-info artgroup)))
598 (setq marked (gnus-uncompress-sequence marked))
599 (when (setq new-marks
600 (delq nil
601 (mapcar
602 #'(lambda (art)
603 (when (memq (cdr art) marked)
604 (car art)))
605 artids)))
606 (nconc
607 (symbol-value
608 (intern
609 (format "gnus-newsgroup-%s"
610 (car (rassq type gnus-article-mark-lists)))))
611 new-marks)))))
612 (setq gnus-newsgroup-active
613 (cons 1 (nnselect-artlist-length gnus-newsgroup-selection)))
614 (gnus-set-active
615 group
616 (cons 1 (nnselect-artlist-length gnus-newsgroup-selection))))
617 headers)
618 ;; If not an imap backend just warp to the original article
619 ;; group and punt back to gnus-summary-refer-thread.
620 (and (gnus-warp-to-article) (gnus-summary-refer-thread))))))
621
622
623(deffoo nnselect-close-group (group &optional _server)
624 (let ((group (nnselect-add-prefix group)))
625 (unless gnus-group-is-exiting-without-update-p
626 (nnselect-push-info group))
627 (setq gnus-newsgroup-selection nil)
628 (when (gnus-ephemeral-group-p group)
629 (gnus-kill-ephemeral-group group)
630 (setq gnus-ephemeral-servers
631 (assq-delete-all 'nnselect gnus-ephemeral-servers)))))
632
633
634(deffoo nnselect-request-create-group (group &optional _server args)
635 (message "Creating nnselect group %s" group)
636 (let* ((group (gnus-group-prefixed-name group '(nnselect "nnselect")))
637 (specs (assq 'nnselect-specs args))
638 (function-spec
639 (or (alist-get 'nnselect-function specs)
640 (intern (completing-read "Function: " obarray #'functionp))))
641 (args-spec
642 (or (alist-get 'nnselect-args specs)
643 (read-from-minibuffer "Args: " nil nil t nil "nil")))
644 (nnselect-specs (list (cons 'nnselect-function function-spec)
645 (cons 'nnselect-args args-spec))))
646 (gnus-group-set-parameter group 'nnselect-specs nnselect-specs)
647 (gnus-group-set-parameter
648 group 'nnselect-artlist
649 (nnselect-compress-artlist (or (alist-get 'nnselect-artlist args)
650 (nnselect-run nnselect-specs))))
651 (nnselect-request-update-info group (gnus-get-info group)))
652 t)
653
654
655(deffoo nnselect-request-type (_group &optional article)
656 (if (and (numberp article) (> article 0))
657 (gnus-request-type
658 (nnselect-article-group article) (nnselect-article-number article))
659 'unknown))
660
661(deffoo nnselect-request-post (&optional _server)
662 (if (not gnus-message-group-art)
663 (nnheader-report 'nnselect "Can't post to an nnselect group")
664 (gnus-request-post
665 (gnus-find-method-for-group
666 (nnselect-article-group (cdr gnus-message-group-art))))))
667
668
669(deffoo nnselect-request-rename-group (_group _new-name &optional _server)
670 t)
671
672
673(deffoo nnselect-request-scan (group _method)
674 (when (and group
675 (gnus-group-get-parameter (nnselect-add-prefix group)
676 'nnselect-rescan t))
677 (nnselect-request-group-scan group)))
678
679
680(deffoo nnselect-request-group-scan (group &optional _server _info)
681 (let* ((group (nnselect-add-prefix group))
682 (artlist (nnselect-run
683 (gnus-group-get-parameter group 'nnselect-specs t))))
684 (gnus-set-active group (cons 1 (nnselect-artlist-length
685 artlist)))
686 (gnus-group-set-parameter
687 group 'nnselect-artlist
688 (nnselect-compress-artlist artlist))))
689
690;; Add any undefined required backend functions
691
692;; (nnoo-define-skeleton nnselect)
693
694;;; Util Code:
695
696(defun gnus-nnselect-group-p (group)
697 "Say whether GROUP is nnselect or not."
698 (or (and (gnus-group-prefixed-p group)
699 (eq 'nnselect (car (gnus-find-method-for-group group))))
700 (eq 'nnselect (car gnus-command-method))))
701
702
703(defun nnselect-run (specs)
704 "Apply nnselect-function to nnselect-args from SPECS.
705Return an article list."
706 (let ((func (alist-get 'nnselect-function specs))
707 (args (alist-get 'nnselect-args specs)))
708 (funcall func args)))
709
710
711(defun nnselect-search-thread (header)
712 "Make an nnselect group containing the thread with article HEADER.
713The current server will be searched. If the registry is
714installed, the server that the registry reports the current
715article came from is also searched."
716 (let* ((query
717 (list (cons 'query (nnimap-make-thread-query header))
718 (cons 'criteria "")))
719 (server
720 (list (list (gnus-method-to-server
721 (gnus-find-method-for-group gnus-newsgroup-name)))))
722 (registry-group (and
723 (bound-and-true-p gnus-registry-enabled)
724 (car (gnus-registry-get-id-key
725 (mail-header-id header) 'group))))
726 (registry-server
727 (and registry-group
728 (gnus-method-to-server
729 (gnus-find-method-for-group registry-group)))))
730 (when registry-server (cl-pushnew (list registry-server) server
731 :test 'equal))
732 (gnus-group-read-ephemeral-group
733 (concat "nnselect-" (message-unique-id))
734 (list 'nnselect "nnselect")
735 nil
736 (cons (current-buffer) gnus-current-window-configuration)
737 ; nil
738 nil nil
739 (list
740 (cons 'nnselect-specs
741 (list
742 (cons 'nnselect-function 'nnir-run-query)
743 (cons 'nnselect-args
744 (list (cons 'nnir-query-spec query)
745 (cons 'nnir-group-spec server)))))
746 (cons 'nnselect-artlist nil)))
747 (gnus-summary-goto-subject (gnus-id-to-article (mail-header-id header)))))
748
749
750
751(defun nnselect-push-info (group)
752 "Copy mark-lists from GROUP to the originating groups."
753 (let ((select-unreads (numbers-by-group gnus-newsgroup-unreads))
754 (select-reads (numbers-by-group
755 (gnus-uncompress-range
756 (gnus-info-read (gnus-get-info group)))))
757 (select-unseen (numbers-by-group gnus-newsgroup-unseen))
758 (gnus-newsgroup-active nil)
759 mark-list type-list)
760 (pcase-dolist (`(,mark . ,type) gnus-article-mark-lists)
761 (when (setq type-list
762 (symbol-value (intern (format "gnus-newsgroup-%s" mark))))
763 (push (cons type
764 (numbers-by-group
765 (gnus-uncompress-range type-list))) mark-list)))
766 (pcase-dolist (`(,artgroup . ,artlist)
767 (numbers-by-group gnus-newsgroup-articles))
768 (let* ((group-info (gnus-get-info artgroup))
769 (old-unread (gnus-list-of-unread-articles artgroup))
770 newmarked)
771 (when group-info
772 (pcase-dolist (`(,_mark . ,type) gnus-article-mark-lists)
773 (let ((select-type
774 (sort
775 (cdr (assoc artgroup (alist-get type mark-list)))
776 '<)) list)
777 (setq list
778 (gnus-uncompress-range
779 (gnus-add-to-range
780 (gnus-remove-from-range
781 (alist-get type (gnus-info-marks group-info))
782 artlist)
783 select-type)))
784
785 (when list
786 ;; Get rid of the entries of the articles that have the
787 ;; default score.
788 (when (and (eq type 'score)
789 gnus-save-score
790 list)
791 (let* ((arts list)
792 (prev (cons nil list))
793 (all prev))
794 (while arts
795 (if (or (not (consp (car arts)))
796 (= (cdar arts) gnus-summary-default-score))
797 (setcdr prev (cdr arts))
798 (setq prev arts))
799 (setq arts (cdr arts)))
800 (setq list (cdr all)))))
801
802 (when (or (eq (gnus-article-mark-to-type type) 'list)
803 (eq (gnus-article-mark-to-type type) 'range))
804 (setq list
805 (gnus-compress-sequence (sort list '<) t)))
806
807 ;; When exiting the group, everything that's previously been
808 ;; unseen is now seen.
809 (when (eq type 'seen)
810 (setq list (gnus-range-add
811 list (cdr (assoc artgroup select-unseen)))))
812
813 (when (or list (eq type 'unexist))
814 (push (cons type list) newmarked))))
815
816 (gnus-atomic-progn
817 ;; Enter these new marks into the info of the group.
818 (if (nthcdr 3 group-info)
819 (setcar (nthcdr 3 group-info) newmarked)
820 ;; Add the marks lists to the end of the info.
821 (when newmarked
822 (setcdr (nthcdr 2 group-info) (list newmarked))))
823
824 ;; Cut off the end of the info if there's nothing else there.
825 (let ((i 5))
826 (while (and (> i 2)
827 (not (nth i group-info)))
828 (when (nthcdr (cl-decf i) group-info)
829 (setcdr (nthcdr i group-info) nil))))
830
831 ;; update read and unread
832 (gnus-update-read-articles
833 artgroup
834 (gnus-uncompress-range
835 (gnus-add-to-range
836 (gnus-remove-from-range
837 old-unread
838 (cdr (assoc artgroup select-reads)))
839 (sort (cdr (assoc artgroup select-unreads)) '<))))
840 (gnus-get-unread-articles-in-group
841 group-info (gnus-active artgroup) t)
842 (gnus-group-update-group artgroup t t)))))))
843
844
845(declare-function gnus-registry-get-id-key "gnus-registry" (id key))
846
847(defun gnus-summary-make-search-group (nnir-extra-parms)
848 "Search a group from the summary buffer.
849Pass NNIR-EXTRA-PARMS on to the search engine."
850 (interactive "P")
851 (gnus-warp-to-article)
852 (let ((spec
853 (list
854 (cons 'nnir-group-spec
855 (list (list
856 (gnus-group-server gnus-newsgroup-name)
857 gnus-newsgroup-name))))))
858 (gnus-group-make-search-group nnir-extra-parms spec)))
859
860
861;; The end.
862(provide 'nnselect)
863
864;;; nnselect.el ends here
diff --git a/lisp/gnus/nnspool.el b/lisp/gnus/nnspool.el
index 33b68fa989e..0b6bba5fea7 100644
--- a/lisp/gnus/nnspool.el
+++ b/lisp/gnus/nnspool.el
@@ -422,7 +422,7 @@ there.")
422 (nnspool-article-pathname nnspool-current-group article)) 422 (nnspool-article-pathname nnspool-current-group article))
423 (nnheader-insert-article-line article) 423 (nnheader-insert-article-line article)
424 (goto-char (point-min)) 424 (goto-char (point-min))
425 (let ((headers (nnheader-parse-head))) 425 (let ((headers (nnheader-parse-head nil t)))
426 (set-buffer cur) 426 (set-buffer cur)
427 (goto-char (point-max)) 427 (goto-char (point-max))
428 (nnheader-insert-nov headers))) 428 (nnheader-insert-nov headers)))