diff options
| -rw-r--r-- | doc/misc/gnus.texi | 259 | ||||
| -rw-r--r-- | etc/NEWS | 19 | ||||
| -rw-r--r-- | lisp/gnus/gnus-agent.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/gnus-cache.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/gnus-cloud.el | 10 | ||||
| -rw-r--r-- | lisp/gnus/gnus-group.el | 54 | ||||
| -rw-r--r-- | lisp/gnus/gnus-msg.el | 120 | ||||
| -rw-r--r-- | lisp/gnus/gnus-registry.el | 13 | ||||
| -rw-r--r-- | lisp/gnus/gnus-srvr.el | 5 | ||||
| -rw-r--r-- | lisp/gnus/gnus-start.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/gnus-sum.el | 295 | ||||
| -rw-r--r-- | lisp/gnus/gnus.el | 7 | ||||
| -rw-r--r-- | lisp/gnus/nndiary.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/nnfolder.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/nnheader.el | 344 | ||||
| -rw-r--r-- | lisp/gnus/nnimap.el | 10 | ||||
| -rw-r--r-- | lisp/gnus/nnir.el | 857 | ||||
| -rw-r--r-- | lisp/gnus/nnmaildir.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/nnml.el | 2 | ||||
| -rw-r--r-- | lisp/gnus/nnselect.el | 864 | ||||
| -rw-r--r-- | lisp/gnus/nnspool.el | 2 |
21 files changed, 1651 insertions, 1222 deletions
diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi index 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 | ||
| 719 | Combined Groups | 719 | Virtual 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 | ||
| 723 | Email Based Diary | 724 | Email 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)} |
| 10409 | Display the full thread where the current article appears | 10410 | Display 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 |
| 10411 | headers in the current group to work, so it usually takes a while. If | 10412 | articles only in the current group. Some backends (currently only |
| 10412 | you 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 |
| 10413 | to @code{invisible} (@pxref{Filling In Threads}). This won't have any | 10414 | cases each header in the current group must be fetched and examined, |
| 10414 | visible effects normally, but it'll make this command work a whole lot | 10415 | so it usually takes a while. If you do it often, you may consider |
| 10415 | faster. Of course, it'll make group entry somewhat slow. | 10416 | setting @code{gnus-fetch-old-headers} to @code{invisible} |
| 10417 | (@pxref{Filling In Threads}). This won't have any visible effects | ||
| 10418 | normally, but it'll make this command work a whole lot faster. Of | ||
| 10419 | course, it'll make group entry somewhat slow. | ||
| 10420 | |||
| 10421 | @vindex gnus-refer-thread-use-search | ||
| 10422 | If @code{gnus-refer-thread-use-search} is non-nil then those backends | ||
| 10423 | that know how to find threads directly will search not just in the | ||
| 10424 | current group but all groups on the same server. | ||
| 10416 | 10425 | ||
| 10417 | @vindex gnus-refer-thread-limit | 10426 | @vindex gnus-refer-thread-limit |
| 10418 | The @code{gnus-refer-thread-limit} variable says how many old (i.e., | 10427 | The @code{gnus-refer-thread-limit} variable says how many old (i.e., |
| @@ -10421,6 +10430,15 @@ fetch when doing this command. The default is 200. If @code{t}, all | |||
| 10421 | the available headers will be fetched. This variable can be overridden | 10430 | the available headers will be fetched. This variable can be overridden |
| 10422 | by giving the @kbd{A T} command a numerical prefix. | 10431 | by giving the @kbd{A T} command a numerical prefix. |
| 10423 | 10432 | ||
| 10433 | @vindex gnus-refer-thread-limit-to-thread | ||
| 10434 | In most cases @code{gnus-refer-thread} adds any articles it finds to | ||
| 10435 | the current summary buffer. (When @code{gnus-refer-thread-use-search} | ||
| 10436 | is true and the initial referral starts from a summary buffer for a | ||
| 10437 | non-virtual group this may not be possible. In this case a new summary | ||
| 10438 | buffer is created holding a virtual group with the result of the thread | ||
| 10439 | search). If @code{gnus-refer-thread-limit-to-thread} is non-nil then | ||
| 10440 | the 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 | ||
| 17840 | Gnus allows combining a mixture of all the other group types into bigger | 17858 | Gnus allows combining articles from many sources, and combinations of |
| 17841 | groups. | 17859 | whole 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 | |||
| 17874 | Gnus provides the @dfn{nnselect} method for creating virtual groups | ||
| 17875 | composed of collections of messages, even when these messages come | ||
| 17876 | from groups that span multiple servers and backends. For the most part | ||
| 17877 | these virtual groups behave like any other group: messages may be | ||
| 17878 | threaded, marked, moved, deleted, copied, etc.; groups may be | ||
| 17879 | ephemeral 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 | |||
| 17883 | The key to using an nnselect group is specifying the messages to | ||
| 17884 | include. 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 | ||
| 17887 | passed to the function, if any. | ||
| 17888 | |||
| 17889 | The function @code{nnselect-function} must return a vector. Each | ||
| 17890 | element of this vector is in turn a 3-element vector corresponding to | ||
| 17891 | one message. The 3 elements are: the fully-qualified group name; the | ||
| 17892 | message number; and a "score" that can be used for additional | ||
| 17893 | sorting. The values for the score are arbitrary, and are not used | ||
| 17894 | directly by the nnselect method---they may, for example, all be set to | ||
| 17895 | 100. | ||
| 17896 | |||
| 17897 | Here 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 | |||
| 17908 | The function is the identity and the argument is just the list of | ||
| 17909 | messages to include in the virtual group. | ||
| 17910 | |||
| 17911 | Or 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 | |||
| 17925 | This creates a group including all flagged messages from all groups on | ||
| 17926 | two imap servers, "home" and "work". | ||
| 17927 | |||
| 17928 | And one last example. Here is a function that runs a search query to | ||
| 17929 | find 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 | |||
| 17946 | Then 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 | |||
| 17954 | will provide a group composed of all messages on the home and work | ||
| 17955 | servers received in the last 7 days. | ||
| 17956 | |||
| 17957 | Refreshing the selection of an nnselect group by running the | ||
| 17958 | @code{nnselect-function} may take a long time to | ||
| 17959 | complete. Consequently nnselect groups are not refreshed by default | ||
| 17960 | when @code{gnus-group-get-new-news} is invoked. In those cases where | ||
| 17961 | running the function is not too time-consuming, a non-nil group | ||
| 17962 | parameter of @code{nnselect-rescan} will allow automatic refreshing. A | ||
| 17963 | refresh can always be invoked manually through | ||
| 17964 | @code{gnus-group-get-new-news-this-group}. | ||
| 17965 | |||
| 17966 | The nnir interface (@pxref{nnir}) includes engines for searching a | ||
| 17967 | variety of backends. While the details of each search engine vary, the | ||
| 17968 | result of an nnir search is always a vector of the sort used by the | ||
| 17969 | nnselect method, and the results of nnir queries are usually viewed | ||
| 17970 | using an nnselect group. Indeed the standard search function | ||
| 17971 | @code{gnus-group-read-ephemeral-search-group} just creates an | ||
| 17972 | ephemeral nnselect group with the appropriate nnir query as the | ||
| 17973 | @code{nnselect-specs}. nnir originally included both the search | ||
| 17974 | engines and the glue to connect search results to gnus. Over time this | ||
| 17975 | glue evolved into the nnselect method. The two had | ||
| 17976 | a mostly amicable parting so that nnselect could pursue its dream of | ||
| 17977 | becoming a fully functioning backend, but occasional conflicts may | ||
| 17978 | still 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 | ||
| 21241 | FIXME: Add a brief overview of Gnus search capabilities. A brief | 21373 | FIXME: A brief comparison of nnir, nnmairix, contrib/gnus-namazu would |
| 21242 | comparison of nnir, nnmairix, contrib/gnus-namazu would be nice | 21374 | be nice. |
| 21243 | as well. | 21375 | |
| 21244 | 21376 | Gnus has various ways of finding articles that match certain criteria | |
| 21245 | This chapter describes tools for searching groups and servers for | 21377 | (from a particular author, on a certain subject, etc). The simplest |
| 21246 | articles matching a query and then retrieving those articles. Gnus | 21378 | method is to enter a group and then either "limit" the summary buffer |
| 21247 | provides a simpler mechanism for searching through articles in a summary buffer | 21379 | to the desired articles using the limiting commands (@xref{Limiting}), |
| 21248 | to find those matching a pattern. @xref{Searching for Articles}. | 21380 | or searching through messages in the summary buffer (@xref{Searching |
| 21381 | for Articles}). | ||
| 21382 | |||
| 21383 | Limiting commands and summary buffer searching work on subsets of the | ||
| 21384 | articles already fetched from the servers, and these commands won’t | ||
| 21385 | query the server for additional articles. While simple, these methods | ||
| 21386 | are therefore inadequate if the desired articles span multiple groups, | ||
| 21387 | or if the group is so large that fetching all articles is | ||
| 21388 | impractical. Many backends (such as imap, notmuch, namazu, etc.) | ||
| 21389 | provide their own facilities to search for articles directly on the | ||
| 21390 | server and gnus can take advantage of these methods. This chapter | ||
| 21391 | describes tools for searching groups and servers for articles matching | ||
| 21392 | a 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 | |||
| 21275 | interface. | 21419 | interface. |
| 21276 | 21420 | ||
| 21277 | The @code{nnimap} search engine should work with no configuration. | 21421 | The @code{nnimap} search engine should work with no configuration. |
| 21278 | Other engines require a local index that needs to be created and | 21422 | Other engines may require a local index that needs to be created and |
| 21279 | maintained outside of Gnus. | 21423 | maintained 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 | ||
| 21285 | In the group buffer typing @kbd{G G} will search the group on the | 21429 | In the group buffer typing @kbd{G G} will search the group on the |
| 21286 | current line by calling @code{gnus-group-make-nnir-group}. This prompts | 21430 | current line by calling @code{gnus-group-make-search-group}. This prompts |
| 21287 | for a query string, creates an ephemeral @code{nnir} group containing | 21431 | for a query string, creates an ephemeral @code{nnselect} group containing |
| 21288 | the articles that match this query, and takes you to a summary buffer | 21432 | the articles that match this query, and takes you to a summary buffer |
| 21289 | showing these articles. Articles may then be read, moved and deleted | 21433 | showing these articles. Articles may then be read, moved and deleted |
| 21290 | using the usual commands. | 21434 | using the usual commands. |
| 21291 | 21435 | ||
| 21292 | The @code{nnir} group made in this way is an @code{ephemeral} group, | 21436 | The @code{nnselect} group made in this way is an @code{ephemeral} |
| 21293 | and some changes are not permanent: aside from reading, moving, and | 21437 | group, and will disappear upon exit from the group. However changes |
| 21294 | deleting, you can't act on the original article. But there is an | 21438 | made in the group are permanently reflected in the real groups from |
| 21295 | alternative: you can @emph{warp} (i.e., jump) to the original group | 21439 | which the articles are drawn. It is occasionally convenient to view |
| 21296 | for the article on the current line with @kbd{A W}, aka | 21440 | articles 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 | 21442 | current line with @kbd{A W}, aka @code{gnus-warp-to-article}. |
| 21299 | to @kbd{A T}, will first warp to the original group before it works | ||
| 21300 | its magic and includes all the articles in the thread. From here you | ||
| 21301 | can read, move and delete articles, but also copy them, alter article | ||
| 21302 | marks, whatever. Go nuts. | ||
| 21303 | 21443 | ||
| 21304 | You say you want to search more than just the group on the current line? | 21444 | You say you want to search more than just the group on the current line? |
| 21305 | No problem: just process-mark the groups you want to search. You want | 21445 | No problem: just process-mark the groups you want to search. You want |
| @@ -21307,14 +21447,14 @@ even more? Calling for an nnir search with the cursor on a topic heading | |||
| 21307 | will search all the groups under that heading. | 21447 | will search all the groups under that heading. |
| 21308 | 21448 | ||
| 21309 | Still not enough? OK, in the server buffer | 21449 | Still 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 |
| 21311 | groups from the server on the current line. Too much? Want to ignore | 21451 | all groups from the server on the current line. Too much? Want to |
| 21312 | certain groups when searching, like spam groups? Just customize | 21452 | ignore certain groups when searching, like spam groups? Just |
| 21313 | @code{nnir-ignored-newsgroups}. | 21453 | customize @code{nnir-ignored-newsgroups}. |
| 21314 | 21454 | ||
| 21315 | One more thing: individual search engines may have special search | 21455 | One more thing: individual search engines may have special search |
| 21316 | features. You can access these special features by giving a prefix-arg | 21456 | features. You can access these special features by giving a prefix-arg |
| 21317 | to @code{gnus-group-make-nnir-group}. If you are searching multiple | 21457 | to @code{gnus-group-make-search-group}. If you are searching multiple |
| 21318 | groups with different search engines you will be prompted for the | 21458 | groups with different search engines you will be prompted for the |
| 21319 | special search features for each engine separately. | 21459 | special 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 | |||
| 21371 | your servers with an @code{nnimap} backend you could change this to | 21511 | your 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 |
| 21577 | Alist of pairs of server backends and search engines. The default | 21716 | Alist of pairs of server backends and search engines. The default |
| 21578 | associations are | 21717 | association is |
| 21579 | @example | 21718 | @example |
| 21580 | (nnimap . imap) | 21719 | (nnimap . imap) |
| 21581 | @end example | 21720 | @end example |
| @@ -21584,32 +21723,6 @@ associations are | |||
| 21584 | A regexp to match newsgroups in the active file that should be skipped | 21723 | A regexp to match newsgroups in the active file that should be skipped |
| 21585 | when searching all groups on a server. | 21724 | when searching all groups on a server. |
| 21586 | 21725 | ||
| 21587 | @item nnir-summary-line-format | ||
| 21588 | The format specification to be used for lines in an nnir summary buffer. | ||
| 21589 | All the items from @code{gnus-summary-line-format} are available, along with | ||
| 21590 | three 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 | |||
| 21598 | If @code{nil} (the default) this will use @code{gnus-summary-line-format}. | ||
| 21599 | |||
| 21600 | @item nnir-retrieve-headers-override-function | ||
| 21601 | If non-@code{nil}, a function that retrieves article headers rather than using | ||
| 21602 | the gnus built-in function. This function takes an article list and | ||
| 21603 | group as arguments and populates the @code{nntp-server-buffer} with the | ||
| 21604 | retrieved headers. It should then return either 'nov or 'headers | ||
| 21605 | indicating the retrieved header format. Failure to retrieve headers | ||
| 21606 | should return @code{nil}. | ||
| 21607 | |||
| 21608 | If this variable is @code{nil}, or if the provided function returns | ||
| 21609 | @code{nil} for a search result, @code{gnus-retrieve-headers} will be | ||
| 21610 | called instead." | ||
| 21611 | |||
| 21612 | |||
| 21613 | @end table | 21726 | @end table |
| 21614 | 21727 | ||
| 21615 | 21728 | ||
| @@ -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' |
| 321 | The newly added nnselect backend allows creating groups from an | ||
| 322 | arbitrary list of articles that may come from multiple groups and | ||
| 323 | servers. These groups generally behave like any other group: they may | ||
| 324 | be ephemeral or persistent, and allow article marking, moving, | ||
| 325 | deletion, etc. Nnselect groups may be created like any other group, | ||
| 326 | but there is also a convenience function for the common case of | ||
| 327 | obtaining the list of articles as a result of a search: | ||
| 328 | 'gnus-group-make-search-group' (G g) that will prompt for an nnir | ||
| 329 | search query and create a dedicated group for that search. As part of | ||
| 330 | this addition, the variable 'nnir-summary-line-format' has been | ||
| 331 | removed; 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' | ||
| 321 | On systems with D-Bus support, it is now possible to register a signal | 338 | On systems with D-Bus support, it is now possible to register a signal |
| 322 | to close all Gnus servers before the system sleeps. | 339 | to 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 | ||
| 3191 | search query and determine the groups to search as follows: if | ||
| 3192 | called from the *Server* buffer search all groups belonging to | ||
| 3193 | the server on the current line; if called from the *Group* buffer | ||
| 3194 | search any marked groups, or the group on the current line, or | ||
| 3195 | all the groups under the current topic. Calling with a prefix-arg | ||
| 3196 | prompts for additional search-engine specific constraints. A | ||
| 3197 | non-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 | |||
| 788 | Consults `gnus-registry-ignored-groups' and | 793 | Consults `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. |
| 1224 | This will be done via the current article's source group based on | 1229 | This 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 |
| 149 | A nil value will only search for thread-related articles in the | 149 | nil value will only search for thread-related articles in the |
| 150 | current group." | 150 | current 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. |
| 5109 | Unscored articles will be counted as having a score of zero." | 5089 | Unscored 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." | |||
| 8702 | When called interactively, ID is the Message-ID of the current | 8560 | When called interactively, ID is the Message-ID of the current |
| 8703 | article. If thread-only is non-nil limit the summary buffer to | 8561 | article. If thread-only is non-nil limit the summary buffer to |
| 8704 | these articles." | 8562 | these 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 |
| 9129 | that know how to search for threads (currently only 'nnimap) | 8988 | know how to search for threads (currently only 'nnimap) a |
| 9130 | a non-numeric prefix arg will use nnir to search the entire | 8989 | non-numeric prefix arg will search the entire |
| 9131 | server; without a prefix arg only the current group is | 8990 | server; without a prefix arg only the current group is |
| 9132 | searched. If the variable `gnus-refer-thread-use-nnir' is | 8991 | searched. If the variable `gnus-refer-thread-use-search' is |
| 9133 | non-nil the prefix arg has the reverse meaning. If no | 8992 | non-nil the prefix arg has the reverse meaning. If no |
| 9134 | backend-specific `request-thread' function is available fetch | 8993 | backend-specific 'request-thread function is available fetch |
| 9135 | LIMIT (the numerical prefix) old headers. If LIMIT is | 8994 | LIMIT (the numerical prefix) old headers. If LIMIT is |
| 9136 | non-numeric or nil fetch the number specified by the | 8995 | non-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 | 188 | By default this is the name of an email header field.") |
| 212 | email 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))'. | ||
| 261 | The category key for a member of the sequence is obtained | ||
| 262 | as `(KEYFUNC member)' and the corresponding element is just | ||
| 263 | `member'. If VALUEFUNC is non-nil, the element of the list | ||
| 264 | is `(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. |
| 297 | be skipped when searching." | 216 | Any newsgroup in the active file matching this regexp will be |
| 217 | skipped 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 | |||
| 305 | All the items from `gnus-summary-line-format' are available, along | ||
| 306 | with 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 | |||
| 312 | If 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 | ||
| 319 | and populates the `nntp-server-buffer' with the retrieved | ||
| 320 | headers. Must return either `nov' or `headers' indicating the | ||
| 321 | retrieved header format. | ||
| 322 | |||
| 323 | If this variable is nil, or if the provided function returns nil for | ||
| 324 | a 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. |
| 331 | the keys in `nnir-imap-search-arguments'. To use raw imap queries | 224 | Must be one of the keys in `nnir-imap-search-arguments'. To use |
| 332 | by default set this to \"imap\"." | 225 | raw 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. |
| 361 | in order to get a group name (albeit with / instead of .). This is a | 254 | Resulting names have '/' in place of '.'. This is a regular |
| 362 | regular expression. | 255 | expression. |
| 363 | 256 | ||
| 364 | This variable is very similar to `nnir-namazu-remove-prefix', except | 257 | This variable is very similar to `nnir-namazu-remove-prefix', except |
| 365 | that it is for swish++, not Namazu." | 258 | that 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. |
| 412 | in order to get a group name (albeit with / instead of .). This is a | 305 | Resulting names have '/' in place of '.'. This is a regular |
| 413 | regular expression. | 306 | expression. |
| 414 | 307 | ||
| 415 | This variable is very similar to `nnir-namazu-remove-prefix', except | 308 | This variable is very similar to `nnir-namazu-remove-prefix', except |
| 416 | that it is for swish-e, not Namazu. | 309 | that 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. |
| 445 | in order to get a group name (albeit with / instead of .). | 338 | Restulting names have '/' in place of '.'. |
| 446 | 339 | ||
| 447 | For example, suppose that HyREX returns file names such as | 340 | For 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. |
| 482 | in order to get a group name (albeit with / instead of .). | 375 | Resulting names have '/' in place of '.'. |
| 483 | 376 | ||
| 484 | For example, suppose that Namazu returns file names such as | 377 | For 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. |
| 513 | in order to get a group name (albeit with / instead of .). This is a | 406 | Resulting names have '/' in place of '.'. This is a regular |
| 514 | regular expression. | 407 | expression. |
| 515 | 408 | ||
| 516 | This variable is very similar to `nnir-namazu-remove-prefix', except | 409 | This variable is very similar to `nnir-namazu-remove-prefix', except |
| 517 | that it is for notmuch, not Namazu." | 410 | that 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. | ||
| 601 | Prompt for a search query and determine the groups to search as | ||
| 602 | follows: if called from the *Server* buffer search all groups | ||
| 603 | belonging to the server on the current line; if called from the | ||
| 604 | *Group* buffer search any marked groups, or the group on the current | ||
| 605 | line, or all the groups under the current topic. Calling with a | ||
| 606 | prefix-arg prompts for additional search-engine specific constraints. | ||
| 607 | A 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. |
| 933 | and if it is non-nil, add it to ARTLIST." | 489 | DIRNAM, ARTNO, SCORE, PREFIX and SERVER are passed to |
| 490 | `nnir-compose-result' to make the vector. Only add the result if | ||
| 491 | non-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. |
| 944 | ready to be added to the list of search results." | 502 | The DIRNAM, ARTICLE, SCORE, PREFIX, and SERVER are used to |
| 945 | 503 | construct 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. |
| 981 | This uses a custom query language parser; see `nnir-imap-make-query' | 539 | Search GROUPS, or all active groups on SRV if GROUPS is nil. |
| 982 | for details on the language and supported extensions." | 540 | This uses a custom query language parser; see |
| 541 | `nnir-imap-make-query' for details on the language and supported | ||
| 542 | extensions." | ||
| 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. |
| 1029 | expression, returning the string query to make. | ||
| 1030 | 588 | ||
| 1031 | This implements a little language designed to return the expected | 589 | This implements a little language designed to return the expected |
| 1032 | results to an arbitrary query string to the end user. | 590 | results to an arbitrary query string to the end user. |
| @@ -1063,7 +621,7 @@ In the future the following will be added to the language: | |||
| 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. |
| 1103 | query language as defined in `nnir-imap-make-query'. | 661 | STRING is based on the IMAP query language as defined in |
| 662 | `nnir-imap-make-query'. | ||
| 1104 | 663 | ||
| 1105 | This involves turning individual tokens into higher level terms | 664 | This involves turning individual tokens into higher level terms |
| 1106 | that the search language can then understand and use." | 665 | that 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. |
| 1151 | at the end of the buffer. If supplied COUNT skips some symbols before | 710 | Return nil if we are at the end of the buffer. If supplied COUNT |
| 1152 | returning the one at the supplied position." | 711 | skips some symbols before returning the one at the supplied |
| 712 | position." | ||
| 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++. |
| 1211 | Returns a vector of (group name, file name) pairs (also vectors, | 771 | Returns a vector of (group name, file name) pairs (also vectors, |
| 1212 | actually). | 772 | actually). |
| 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. |
| 1301 | Returns a vector of (group name, file name) pairs (also vectors, | 861 | Returns a vector of (group name, file name) pairs (also vectors, |
| 1302 | actually). | 862 | actually). |
| 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. |
| 1467 | Returns a vector of (group name, file name) pairs (also vectors, | 1028 | Returns a vector of (group name, file name) pairs (also vectors, |
| 1468 | actually). | 1029 | actually). |
| 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. |
| 1537 | Returns a vector of (group name, file name) pairs (also vectors, | 1098 | Returns a vector of (group name, file name) pairs (also vectors, |
| 1538 | actually). If GROUPS is a list of group names, use them to | 1099 | actually). If GROUPS is a list of group names, use them to |
| 1539 | construct path: search terms (see the variable | 1100 | construct 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. |
| 1270 | Parameters 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. | ||
| 1774 | The current server will be searched. If the registry is installed, | ||
| 1775 | the server that the registry reports the current article came from | ||
| 1776 | is 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 | 1365 | Query 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. | ||
| 158 | Returns a list of the form | ||
| 159 | `((key1 (element11 element12)) (key2 (element21 element22))'. | ||
| 160 | The category key for a member of the sequence is obtained | ||
| 161 | as `(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. | ||
| 243 | The retrieved headers should populate the `nntp-server-buffer'. | ||
| 244 | Returns either the retrieved header format 'nov or 'headers. | ||
| 245 | |||
| 246 | If 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. | ||
| 705 | Return 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. | ||
| 713 | The current server will be searched. If the registry is | ||
| 714 | installed, the server that the registry reports the current | ||
| 715 | article 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. | ||
| 849 | Pass 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))) |