*** empty log message ***
[gnus] / lisp / gnus-sum.el
1 ;;; gnus-sum.el --- summary mode commands for Gnus
2 ;; Copyright (C) 1996,97,98 Free Software Foundation, Inc.
3
4 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
5 ;; Keywords: news
6
7 ;; This file is part of GNU Emacs.
8
9 ;; GNU Emacs is free software; you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation; either version 2, or (at your option)
12 ;; any later version.
13
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 ;; GNU General Public License for more details.
18
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 ;; Boston, MA 02111-1307, USA.
23
24 ;;; Commentary:
25
26 ;;; Code:
27
28 (eval-when-compile (require 'cl))
29
30 (require 'gnus)
31 (require 'gnus-group)
32 (require 'gnus-spec)
33 (require 'gnus-range)
34 (require 'gnus-int)
35 (require 'gnus-undo)
36 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
37
38 (defcustom gnus-kill-summary-on-exit t
39   "*If non-nil, kill the summary buffer when you exit from it.
40 If nil, the summary will become a \"*Dead Summary*\" buffer, and
41 it will be killed sometime later."
42   :group 'gnus-summary-exit
43   :type 'boolean)
44
45 (defcustom gnus-fetch-old-headers nil
46   "*Non-nil means that Gnus will try to build threads by grabbing old headers.
47 If an unread article in the group refers to an older, already read (or
48 just marked as read) article, the old article will not normally be
49 displayed in the Summary buffer.  If this variable is non-nil, Gnus
50 will attempt to grab the headers to the old articles, and thereby
51 build complete threads.  If it has the value `some', only enough
52 headers to connect otherwise loose threads will be displayed.  This
53 variable can also be a number.  In that case, no more than that number
54 of old headers will be fetched.  If it has the value `invisible', all
55 old headers will be fetched, but none will be displayed.
56
57 The server has to support NOV for any of this to work."
58   :group 'gnus-thread
59   :type '(choice (const :tag "off" nil)
60                  (const some)
61                  number
62                  (sexp :menu-tag "other" t)))
63
64 (defcustom gnus-refer-thread-limit 200
65   "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
66 If t, fetch all the available old headers."
67   :group 'gnus-thread
68   :type '(choice number
69                  (sexp :menu-tag "other" t)))
70
71 (defcustom gnus-summary-make-false-root 'adopt
72   "*nil means that Gnus won't gather loose threads.
73 If the root of a thread has expired or been read in a previous
74 session, the information necessary to build a complete thread has been
75 lost.  Instead of having many small sub-threads from this original thread
76 scattered all over the summary buffer, Gnus can gather them.
77
78 If non-nil, Gnus will try to gather all loose sub-threads from an
79 original thread into one large thread.
80
81 If this variable is non-nil, it should be one of `none', `adopt',
82 `dummy' or `empty'.
83
84 If this variable is `none', Gnus will not make a false root, but just
85 present the sub-threads after another.
86 If this variable is `dummy', Gnus will create a dummy root that will
87 have all the sub-threads as children.
88 If this variable is `adopt', Gnus will make one of the \"children\"
89 the parent and mark all the step-children as such.
90 If this variable is `empty', the \"children\" are printed with empty
91 subject fields.  (Or rather, they will be printed with a string
92 given by the `gnus-summary-same-subject' variable.)"
93   :group 'gnus-thread
94   :type '(choice (const :tag "off" nil)
95                  (const none)
96                  (const dummy)
97                  (const adopt)
98                  (const empty)))
99
100 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
101   "*A regexp to match subjects to be excluded from loose thread gathering.
102 As loose thread gathering is done on subjects only, that means that
103 there can be many false gatherings performed.  By rooting out certain
104 common subjects, gathering might become saner."
105   :group 'gnus-thread
106   :type 'regexp)
107
108 (defcustom gnus-summary-gather-subject-limit nil
109   "*Maximum length of subject comparisons when gathering loose threads.
110 Use nil to compare full subjects.  Setting this variable to a low
111 number will help gather threads that have been corrupted by
112 newsreaders chopping off subject lines, but it might also mean that
113 unrelated articles that have subject that happen to begin with the
114 same few characters will be incorrectly gathered.
115
116 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
117 comparing subjects."
118   :group 'gnus-thread
119   :type '(choice (const :tag "off" nil)
120                  (const fuzzy)
121                  (sexp :menu-tag "on" t)))
122
123 (defcustom gnus-simplify-subject-functions nil
124   "List of functions taking a string argument that simplify subjects.
125 The functions are applied recursively.
126
127 Useful functions to put in this list include: `gnus-simplify-subject-re',
128 `gnus-simplify-subject-fuzzy' and `gnus-simplify-whitespace'."
129   :group 'gnus-thread
130   :type '(repeat function))
131
132 (defcustom gnus-simplify-ignored-prefixes nil
133   "*Regexp, matches for which are removed from subject lines when simplifying fuzzily."
134   :group 'gnus-thread
135   :type '(choice (const :tag "off" nil)
136                  regexp))
137
138 (defcustom gnus-build-sparse-threads nil
139   "*If non-nil, fill in the gaps in threads.
140 If `some', only fill in the gaps that are needed to tie loose threads
141 together.  If `more', fill in all leaf nodes that Gnus can find.  If
142 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
143   :group 'gnus-thread
144   :type '(choice (const :tag "off" nil)
145                  (const some)
146                  (const more)
147                  (sexp :menu-tag "all" t)))
148
149 (defcustom gnus-summary-thread-gathering-function
150   'gnus-gather-threads-by-subject
151   "*Function used for gathering loose threads.
152 There are two pre-defined functions: `gnus-gather-threads-by-subject',
153 which only takes Subjects into consideration; and
154 `gnus-gather-threads-by-references', which compared the References
155 headers of the articles to find matches."
156   :group 'gnus-thread
157   :type '(radio (function-item gnus-gather-threads-by-subject)
158                 (function-item gnus-gather-threads-by-references)
159                 (function :tag "other")))
160
161 (defcustom gnus-summary-same-subject ""
162   "*String indicating that the current article has the same subject as the previous.
163 This variable will only be used if the value of
164 `gnus-summary-make-false-root' is `empty'."
165   :group 'gnus-summary-format
166   :type 'string)
167
168 (defcustom gnus-summary-goto-unread t
169   "*If t, marking commands will go to the next unread article.
170 If `never', commands that usually go to the next unread article, will
171 go to the next article, whether it is read or not.
172 If nil, only the marking commands will go to the next (un)read article."
173   :group 'gnus-summary-marks
174   :link '(custom-manual "(gnus)Setting Marks")
175   :type '(choice (const :tag "off" nil)
176                  (const never)
177                  (sexp :menu-tag "on" t)))
178
179 (defcustom gnus-summary-default-score 0
180   "*Default article score level.
181 All scores generated by the score files will be added to this score.
182 If this variable is nil, scoring will be disabled."
183   :group 'gnus-score-default
184   :type '(choice (const :tag "disable")
185                  integer))
186
187 (defcustom gnus-summary-zcore-fuzz 0
188   "*Fuzziness factor for the zcore in the summary buffer.
189 Articles with scores closer than this to `gnus-summary-default-score'
190 will not be marked."
191   :group 'gnus-summary-format
192   :type 'integer)
193
194 (defcustom gnus-simplify-subject-fuzzy-regexp nil
195   "*Strings to be removed when doing fuzzy matches.
196 This can either be a regular expression or list of regular expressions
197 that will be removed from subject strings if fuzzy subject
198 simplification is selected."
199   :group 'gnus-thread
200   :type '(repeat regexp))
201
202 (defcustom gnus-show-threads t
203   "*If non-nil, display threads in summary mode."
204   :group 'gnus-thread
205   :type 'boolean)
206
207 (defcustom gnus-thread-hide-subtree nil
208   "*If non-nil, hide all threads initially.
209 If threads are hidden, you have to run the command
210 `gnus-summary-show-thread' by hand or use `gnus-select-article-hook'
211 to expose hidden threads."
212   :group 'gnus-thread
213   :type 'boolean)
214
215 (defcustom gnus-thread-hide-killed t
216   "*If non-nil, hide killed threads automatically."
217   :group 'gnus-thread
218   :type 'boolean)
219
220 (defcustom gnus-thread-ignore-subject nil
221   "*If non-nil, ignore subjects and do all threading based on the Reference header.
222 If nil, which is the default, articles that have different subjects
223 from their parents will start separate threads."
224   :group 'gnus-thread
225   :type 'boolean)
226
227 (defcustom gnus-thread-operation-ignore-subject t
228   "*If non-nil, subjects will be ignored when doing thread commands.
229 This affects commands like `gnus-summary-kill-thread' and
230 `gnus-summary-lower-thread'.
231
232 If this variable is nil, articles in the same thread with different
233 subjects will not be included in the operation in question.  If this
234 variable is `fuzzy', only articles that have subjects that are fuzzily
235 equal will be included."
236   :group 'gnus-thread
237   :type '(choice (const :tag "off" nil)
238                  (const fuzzy)
239                  (sexp :tag "on" t)))
240
241 (defcustom gnus-thread-indent-level 4
242   "*Number that says how much each sub-thread should be indented."
243   :group 'gnus-thread
244   :type 'integer)
245
246 (defcustom gnus-auto-extend-newsgroup t
247   "*If non-nil, extend newsgroup forward and backward when requested."
248   :group 'gnus-summary-choose
249   :type 'boolean)
250
251 (defcustom gnus-auto-select-first t
252   "*If nil, don't select the first unread article when entering a group.
253 If this variable is `best', select the highest-scored unread article
254 in the group.  If neither nil nor `best', select the first unread
255 article.
256
257 If you want to prevent automatic selection of the first unread article
258 in some newsgroups, set the variable to nil in
259 `gnus-select-group-hook'."
260   :group 'gnus-group-select
261   :type '(choice (const :tag "none" nil)
262                  (const best)
263                  (sexp :menu-tag "first" t)))
264
265 (defcustom gnus-auto-select-next t
266   "*If non-nil, offer to go to the next group from the end of the previous.
267 If the value is t and the next newsgroup is empty, Gnus will exit
268 summary mode and go back to group mode.  If the value is neither nil
269 nor t, Gnus will select the following unread newsgroup.  In
270 particular, if the value is the symbol `quietly', the next unread
271 newsgroup will be selected without any confirmation, and if it is
272 `almost-quietly', the next group will be selected without any
273 confirmation if you are located on the last article in the group.
274 Finally, if this variable is `slightly-quietly', the `Z n' command
275 will go to the next group without confirmation."
276   :group 'gnus-summary-maneuvering
277   :type '(choice (const :tag "off" nil)
278                  (const quietly)
279                  (const almost-quietly)
280                  (const slightly-quietly)
281                  (sexp :menu-tag "on" t)))
282
283 (defcustom gnus-auto-select-same nil
284   "*If non-nil, select the next article with the same subject."
285   :group 'gnus-summary-maneuvering
286   :type 'boolean)
287
288 (defcustom gnus-summary-check-current nil
289   "*If non-nil, consider the current article when moving.
290 The \"unread\" movement commands will stay on the same line if the
291 current article is unread."
292   :group 'gnus-summary-maneuvering
293   :type 'boolean)
294
295 (defcustom gnus-auto-center-summary t
296   "*If non-nil, always center the current summary buffer.
297 In particular, if `vertical' do only vertical recentering.  If non-nil
298 and non-`vertical', do both horizontal and vertical recentering."
299   :group 'gnus-summary-maneuvering
300   :type '(choice (const :tag "none" nil)
301                  (const vertical)
302                  (sexp :menu-tag "both" t)))
303
304 (defcustom gnus-show-all-headers nil
305   "*If non-nil, don't hide any headers."
306   :group 'gnus-article-hiding
307   :group 'gnus-article-headers
308   :type 'boolean)
309
310 (defcustom gnus-summary-ignore-duplicates nil
311   "*If non-nil, ignore articles with identical Message-ID headers."
312   :group 'gnus-summary
313   :type 'boolean)
314   
315 (defcustom gnus-single-article-buffer t
316   "*If non-nil, display all articles in the same buffer.
317 If nil, each group will get its own article buffer."
318   :group 'gnus-article-various
319   :type 'boolean)
320
321 (defcustom gnus-break-pages t
322   "*If non-nil, do page breaking on articles.
323 The page delimiter is specified by the `gnus-page-delimiter'
324 variable."
325   :group 'gnus-article-various
326   :type 'boolean)
327
328 (defcustom gnus-show-mime nil
329   "*If non-nil, do mime processing of articles.
330 The articles will simply be fed to the function given by
331 `gnus-show-mime-method'."
332   :group 'gnus-article-mime
333   :type 'boolean)
334
335 (defcustom gnus-move-split-methods nil
336   "*Variable used to suggest where articles are to be moved to.
337 It uses the same syntax as the `gnus-split-methods' variable."
338   :group 'gnus-summary-mail
339   :type '(repeat (choice (list :value (fun) function)
340                          (cons :value ("" "") regexp (repeat string))
341                          (sexp :value nil))))
342
343 (defcustom gnus-unread-mark ? 
344   "*Mark used for unread articles."
345   :group 'gnus-summary-marks
346   :type 'character)
347
348 (defcustom gnus-ticked-mark ?!
349   "*Mark used for ticked articles."
350   :group 'gnus-summary-marks
351   :type 'character)
352
353 (defcustom gnus-dormant-mark ??
354   "*Mark used for dormant articles."
355   :group 'gnus-summary-marks
356   :type 'character)
357
358 (defcustom gnus-del-mark ?r
359   "*Mark used for del'd articles."
360   :group 'gnus-summary-marks
361   :type 'character)
362
363 (defcustom gnus-read-mark ?R
364   "*Mark used for read articles."
365   :group 'gnus-summary-marks
366   :type 'character)
367
368 (defcustom gnus-expirable-mark ?E
369   "*Mark used for expirable articles."
370   :group 'gnus-summary-marks
371   :type 'character)
372
373 (defcustom gnus-killed-mark ?K
374   "*Mark used for killed articles."
375   :group 'gnus-summary-marks
376   :type 'character)
377
378 (defcustom gnus-souped-mark ?F
379   "*Mark used for killed articles."
380   :group 'gnus-summary-marks
381   :type 'character)
382
383 (defcustom gnus-kill-file-mark ?X
384   "*Mark used for articles killed by kill files."
385   :group 'gnus-summary-marks
386   :type 'character)
387
388 (defcustom gnus-low-score-mark ?Y
389   "*Mark used for articles with a low score."
390   :group 'gnus-summary-marks
391   :type 'character)
392
393 (defcustom gnus-catchup-mark ?C
394   "*Mark used for articles that are caught up."
395   :group 'gnus-summary-marks
396   :type 'character)
397
398 (defcustom gnus-replied-mark ?A
399   "*Mark used for articles that have been replied to."
400   :group 'gnus-summary-marks
401   :type 'character)
402
403 (defcustom gnus-cached-mark ?*
404   "*Mark used for articles that are in the cache."
405   :group 'gnus-summary-marks
406   :type 'character)
407
408 (defcustom gnus-saved-mark ?S
409   "*Mark used for articles that have been saved to."
410   :group 'gnus-summary-marks
411   :type 'character)
412
413 (defcustom gnus-ancient-mark ?O
414   "*Mark used for ancient articles."
415   :group 'gnus-summary-marks
416   :type 'character)
417
418 (defcustom gnus-sparse-mark ?Q
419   "*Mark used for sparsely reffed articles."
420   :group 'gnus-summary-marks
421   :type 'character)
422
423 (defcustom gnus-canceled-mark ?G
424   "*Mark used for canceled articles."
425   :group 'gnus-summary-marks
426   :type 'character)
427
428 (defcustom gnus-duplicate-mark ?M
429   "*Mark used for duplicate articles."
430   :group 'gnus-summary-marks
431   :type 'character)
432
433 (defcustom gnus-undownloaded-mark ?@
434   "*Mark used for articles that weren't downloaded."
435   :group 'gnus-summary-marks
436   :type 'character)
437
438 (defcustom gnus-downloadable-mark ?%
439   "*Mark used for articles that are to be downloaded."
440   :group 'gnus-summary-marks
441   :type 'character)
442
443 (defcustom gnus-unsendable-mark ?=
444   "*Mark used for articles that won't be sent."
445   :group 'gnus-summary-marks
446   :type 'character)
447
448 (defcustom gnus-score-over-mark ?+
449   "*Score mark used for articles with high scores."
450   :group 'gnus-summary-marks
451   :type 'character)
452
453 (defcustom gnus-score-below-mark ?-
454   "*Score mark used for articles with low scores."
455   :group 'gnus-summary-marks
456   :type 'character)
457
458 (defcustom gnus-empty-thread-mark ? 
459   "*There is no thread under the article."
460   :group 'gnus-summary-marks
461   :type 'character)
462
463 (defcustom gnus-not-empty-thread-mark ?=
464   "*There is a thread under the article."
465   :group 'gnus-summary-marks
466   :type 'character)
467
468 (defcustom gnus-view-pseudo-asynchronously nil
469   "*If non-nil, Gnus will view pseudo-articles asynchronously."
470   :group 'gnus-extract-view
471   :type 'boolean)
472
473 (defcustom gnus-view-pseudos nil
474   "*If `automatic', pseudo-articles will be viewed automatically.
475 If `not-confirm', pseudos will be viewed automatically, and the user
476 will not be asked to confirm the command."
477   :group 'gnus-extract-view
478   :type '(choice (const :tag "off" nil)
479                  (const automatic)
480                  (const not-confirm)))
481
482 (defcustom gnus-view-pseudos-separately t
483   "*If non-nil, one pseudo-article will be created for each file to be viewed.
484 If nil, all files that use the same viewing command will be given as a
485 list of parameters to that command."
486   :group 'gnus-extract-view
487   :type 'boolean)
488
489 (defcustom gnus-insert-pseudo-articles t
490   "*If non-nil, insert pseudo-articles when decoding articles."
491   :group 'gnus-extract-view
492   :type 'boolean)
493
494 (defcustom gnus-summary-dummy-line-format
495   "  %(:                          :%) %S\n"
496   "*The format specification for the dummy roots in the summary buffer.
497 It works along the same lines as a normal formatting string,
498 with some simple extensions.
499
500 %S  The subject"
501   :group 'gnus-threading
502   :type 'string)
503
504 (defcustom gnus-summary-mode-line-format "Gnus: %%b [%A] %Z"
505   "*The format specification for the summary mode line.
506 It works along the same lines as a normal formatting string,
507 with some simple extensions:
508
509 %G  Group name
510 %p  Unprefixed group name
511 %A  Current article number
512 %z  Current article score
513 %V  Gnus version
514 %U  Number of unread articles in the group
515 %e  Number of unselected articles in the group
516 %Z  A string with unread/unselected article counts
517 %g  Shortish group name
518 %S  Subject of the current article
519 %u  User-defined spec
520 %s  Current score file name
521 %d  Number of dormant articles
522 %r  Number of articles that have been marked as read in this session
523 %E  Number of articles expunged by the score files"
524   :group 'gnus-summary-format
525   :type 'string)
526
527 (defcustom gnus-summary-mark-below 0
528   "*Mark all articles with a score below this variable as read.
529 This variable is local to each summary buffer and usually set by the
530 score file."
531   :group 'gnus-score-default
532   :type 'integer)
533
534 (defcustom gnus-article-sort-functions '(gnus-article-sort-by-number)
535   "*List of functions used for sorting articles in the summary buffer.
536 This variable is only used when not using a threaded display."
537   :group 'gnus-summary-sort
538   :type '(repeat (choice (function-item gnus-article-sort-by-number)
539                          (function-item gnus-article-sort-by-author)
540                          (function-item gnus-article-sort-by-subject)
541                          (function-item gnus-article-sort-by-date)
542                          (function-item gnus-article-sort-by-score)
543                          (function :tag "other"))))
544
545 (defcustom gnus-thread-sort-functions '(gnus-thread-sort-by-number)
546   "*List of functions used for sorting threads in the summary buffer.
547 By default, threads are sorted by article number.
548
549 Each function takes two threads and return non-nil if the first thread
550 should be sorted before the other.  If you use more than one function,
551 the primary sort function should be the last.  You should probably
552 always include `gnus-thread-sort-by-number' in the list of sorting
553 functions -- preferably first.
554
555 Ready-made functions include `gnus-thread-sort-by-number',
556 `gnus-thread-sort-by-author', `gnus-thread-sort-by-subject',
557 `gnus-thread-sort-by-date', `gnus-thread-sort-by-score' and
558 `gnus-thread-sort-by-total-score' (see `gnus-thread-score-function')."
559   :group 'gnus-summary-sort
560   :type '(repeat (choice (function-item gnus-thread-sort-by-number)
561                          (function-item gnus-thread-sort-by-author)
562                          (function-item gnus-thread-sort-by-subject)
563                          (function-item gnus-thread-sort-by-date)
564                          (function-item gnus-thread-sort-by-score)
565                          (function-item gnus-thread-sort-by-total-score)
566                          (function :tag "other"))))
567
568 (defcustom gnus-thread-score-function '+
569   "*Function used for calculating the total score of a thread.
570
571 The function is called with the scores of the article and each
572 subthread and should then return the score of the thread.
573
574 Some functions you can use are `+', `max', or `min'."
575   :group 'gnus-summary-sort
576   :type 'function)
577
578 (defcustom gnus-summary-expunge-below nil
579   "All articles that have a score less than this variable will be expunged.
580 This variable is local to the summary buffers."
581   :group 'gnus-score-default
582   :type '(choice (const :tag "off" nil)
583                  integer))
584
585 (defcustom gnus-thread-expunge-below nil
586   "All threads that have a total score less than this variable will be expunged.
587 See `gnus-thread-score-function' for en explanation of what a
588 \"thread score\" is.
589