1 ;;; gnus-sum.el --- summary mode commands for Gnus
3 ;; Copyright (C) 1996-2016 Free Software Foundation, Inc.
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
8 ;; This file is part of GNU Emacs.
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.
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.
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/>.
30 (when (featurep 'xemacs)
31 (require 'easy-mmode))) ; for `define-minor-mode'
33 (defvar tool-bar-mode)
34 (defvar gnus-tmp-header)
47 (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
48 (autoload 'gnus-cache-write-active "gnus-cache")
49 (autoload 'gnus-mailing-list-insinuate "gnus-ml" nil t)
50 (autoload 'turn-on-gnus-mailing-list-mode "gnus-ml" nil t)
51 (autoload 'gnus-pick-line-number "gnus-salt" nil t)
52 (autoload 'mm-uu-dissect "mm-uu")
53 (autoload 'gnus-article-outlook-deuglify-article "deuglify"
54 "Deuglify broken Outlook (Express) articles and redisplay."
56 (autoload 'gnus-article-outlook-unwrap-lines "deuglify" nil t)
57 (autoload 'gnus-article-outlook-repair-attribution "deuglify" nil t)
58 (autoload 'gnus-article-outlook-rearrange-citation "deuglify" nil t)
59 (autoload 'nnir-article-rsv "nnir" nil nil 'macro)
60 (autoload 'nnir-article-group "nnir" nil nil 'macro)
62 (defcustom gnus-kill-summary-on-exit t
63 "*If non-nil, kill the summary buffer when you exit from it.
64 If nil, the summary will become a \"*Dead Summary*\" buffer, and
65 it will be killed sometime later."
66 :group 'gnus-summary-exit
69 (defcustom gnus-summary-next-group-on-exit t
70 "If non-nil, go to the next unread newsgroup on summary exit.
71 See `gnus-group-goto-unread'."
72 :link '(custom-manual "(gnus)Group Maneuvering")
73 :group 'gnus-summary-exit
74 :version "23.1" ;; No Gnus
77 (defcustom gnus-summary-stop-at-end-of-message nil
78 "If non-nil, don't select the next message when using `SPC'."
79 :link '(custom-manual "(gnus)Group Maneuvering")
80 :group 'gnus-summary-maneuvering
84 (defcustom gnus-fetch-old-headers nil
85 "*Non-nil means that Gnus will try to build threads by grabbing old headers.
86 If an unread article in the group refers to an older, already
87 read (or just marked as read) article, the old article will not
88 normally be displayed in the Summary buffer. If this variable is
89 t, Gnus will attempt to grab the headers to the old articles, and
90 thereby build complete threads. If it has the value `some', all
91 old headers will be fetched but only enough headers to connect
92 otherwise loose threads will be displayed. This variable can
93 also be a number. In that case, no more than that number of old
94 headers will be fetched. If it has the value `invisible', all
95 old headers will be fetched, but none will be displayed.
97 The server has to support NOV for any of this to work.
99 This feature can seriously impact performance it ignores all
100 locally cached header entries. Setting it to t for groups for a
101 server that doesn't expire articles (such as news.gmane.org),
102 leads to very slow summary generation."
104 :type '(choice (const :tag "off" nil)
109 (sexp :menu-tag "other" t)))
111 (defcustom gnus-refer-thread-limit 500
112 "*The number of old headers to fetch when doing \\<gnus-summary-mode-map>\\[gnus-summary-refer-thread].
113 If t, fetch all the available old headers."
115 :type '(choice number
116 (sexp :menu-tag "other" t)))
118 (defcustom gnus-refer-thread-use-nnir nil
119 "*Use nnir to search an entire server when referring threads. A
120 nil value will only search for thread-related articles in the
126 (defcustom gnus-summary-make-false-root 'adopt
127 "*nil means that Gnus won't gather loose threads.
128 If the root of a thread has expired or been read in a previous
129 session, the information necessary to build a complete thread has been
130 lost. Instead of having many small sub-threads from this original thread
131 scattered all over the summary buffer, Gnus can gather them.
133 If non-nil, Gnus will try to gather all loose sub-threads from an
134 original thread into one large thread.
136 If this variable is non-nil, it should be one of `none', `adopt',
139 If this variable is `none', Gnus will not make a false root, but just
140 present the sub-threads after another.
141 If this variable is `dummy', Gnus will create a dummy root that will
142 have all the sub-threads as children.
143 If this variable is `adopt', Gnus will make one of the \"children\"
144 the parent and mark all the step-children as such.
145 If this variable is `empty', the \"children\" are printed with empty
146 subject fields. (Or rather, they will be printed with a string
147 given by the `gnus-summary-same-subject' variable.)"
149 :type '(choice (const :tag "off" nil)
155 (defcustom gnus-summary-make-false-root-always nil
156 "Always make a false dummy root."
161 (defcustom gnus-summary-gather-exclude-subject "^ *$\\|^(none)$"
162 "*A regexp to match subjects to be excluded from loose thread gathering.
163 As loose thread gathering is done on subjects only, that means that
164 there can be many false gatherings performed. By rooting out certain
165 common subjects, gathering might become saner."
169 (defcustom gnus-summary-gather-subject-limit nil
170 "*Maximum length of subject comparisons when gathering loose threads.
171 Use nil to compare full subjects. Setting this variable to a low
172 number will help gather threads that have been corrupted by
173 newsreaders chopping off subject lines, but it might also mean that
174 unrelated articles that have subject that happen to begin with the
175 same few characters will be incorrectly gathered.
177 If this variable is `fuzzy', Gnus will use a fuzzy algorithm when
180 :type '(choice (const :tag "off" nil)
182 (sexp :menu-tag "on" t)))
184 (defcustom gnus-simplify-subject-functions nil
185 "List of functions taking a string argument that simplify subjects.
186 The functions are applied recursively.
188 Useful functions to put in this list include:
189 `gnus-simplify-subject-re', `gnus-simplify-subject-fuzzy',
190 `gnus-simplify-whitespace', and `gnus-simplify-all-whitespace'."
192 :type '(repeat function))
194 (defcustom gnus-simplify-ignored-prefixes nil
195 "*Remove matches for this regexp from subject lines when simplifying fuzzily."
197 :type '(choice (const :tag "off" nil)
200 (defcustom gnus-build-sparse-threads nil
201 "*If non-nil, fill in the gaps in threads.
202 If `some', only fill in the gaps that are needed to tie loose threads
203 together. If `more', fill in all leaf nodes that Gnus can find. If
204 non-nil and non-`some', fill in all gaps that Gnus manages to guess."
206 :type '(choice (const :tag "off" nil)
209 (sexp :menu-tag "all" t)))
211 (defcustom gnus-summary-thread-gathering-function
212 'gnus-gather-threads-by-subject
213 "*Function used for gathering loose threads.
214 There are two pre-defined functions: `gnus-gather-threads-by-subject',
215 which only takes Subjects into consideration; and
216 `gnus-gather-threads-by-references', which compared the References
217 headers of the articles to find matches."
219 :type '(radio (function-item gnus-gather-threads-by-subject)
220 (function-item gnus-gather-threads-by-references)
221 (function :tag "other")))
223 (defcustom gnus-summary-same-subject ""
224 "*String indicating that the current article has the same subject as the previous.
225 This variable will only be used if the value of
226 `gnus-summary-make-false-root' is `empty'."
227 :group 'gnus-summary-format
230 (defcustom gnus-summary-goto-unread nil
231 "*If t, many commands will go to the next unread article.
232 This applies to marking commands as well as other commands that
233 \"naturally\" select the next article, like, for instance, `SPC' at
234 the end of an article.
236 If nil, the marking commands do NOT go to the next unread article
237 \(they go to the next article instead). If `never', commands that
238 usually go to the next unread article, will go to the next article,
239 whether it is read or not."
241 :group 'gnus-summary-marks
242 :link '(custom-manual "(gnus)Setting Marks")
243 :type '(choice (const :tag "off" nil)
245 (sexp :menu-tag "on" t)))
247 (defcustom gnus-summary-default-score 0
248 "*Default article score level.
249 All scores generated by the score files will be added to this score.
250 If this variable is nil, scoring will be disabled."
251 :group 'gnus-score-default
252 :type '(choice (const :tag "disable")
255 (defcustom gnus-summary-default-high-score 0
256 "*Default threshold for a high scored article.
257 An article will be highlighted as high scored if its score is greater
260 :group 'gnus-score-default
263 (defcustom gnus-summary-default-low-score 0
264 "*Default threshold for a low scored article.
265 An article will be highlighted as low scored if its score is smaller
268 :group 'gnus-score-default
271 (defcustom gnus-summary-zcore-fuzz 0
272 "*Fuzziness factor for the zcore in the summary buffer.
273 Articles with scores closer than this to `gnus-summary-default-score'
275 :group 'gnus-summary-format
278 (defcustom gnus-simplify-subject-fuzzy-regexp nil
279 "*Strings to be removed when doing fuzzy matches.
280 This can either be a regular expression or list of regular expressions
281 that will be removed from subject strings if fuzzy subject
282 simplification is selected."
284 :type '(repeat regexp))
286 (defcustom gnus-show-threads t
287 "*If non-nil, display threads in summary mode."
291 (defcustom gnus-thread-hide-subtree nil
292 "*If non-nil, hide all threads initially.
293 This can be a predicate specifier which says which threads to hide.
294 If threads are hidden, you have to run the command
295 `gnus-summary-show-thread' by hand or select an article."
297 :type '(radio (sexp :format "Non-nil\n"
298 :match (lambda (widget value)
299 (not (or (consp value) (functionp value))))
302 (sexp :tag "Predicate specifier")))
304 (defcustom gnus-thread-hide-killed t
305 "*If non-nil, hide killed threads automatically."
309 (defcustom gnus-thread-ignore-subject t
310 "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
311 If nil, articles that have different subjects from their parents will
312 start separate threads."
316 (defcustom gnus-thread-operation-ignore-subject t
317 "*If non-nil, subjects will be ignored when doing thread commands.
318 This affects commands like `gnus-summary-kill-thread' and
319 `gnus-summary-lower-thread'.
321 If this variable is nil, articles in the same thread with different
322 subjects will not be included in the operation in question. If this
323 variable is `fuzzy', only articles that have subjects that are fuzzily
324 equal will be included."
326 :type '(choice (const :tag "off" nil)
330 (defcustom gnus-thread-indent-level 4
331 "*Number that says how much each sub-thread should be indented."
335 (defcustom gnus-auto-extend-newsgroup t
336 "*If non-nil, extend newsgroup forward and backward when requested."
337 :group 'gnus-summary-choose
340 (defcustom gnus-auto-select-first t
341 "If non-nil, select an article on group entry.
342 An article is selected automatically when entering a group
343 e.g. with \\<gnus-group-mode-map>\\[gnus-group-read-group], or via `gnus-summary-next-page' or
344 `gnus-summary-catchup-and-goto-next-group'.
346 Which article is selected is controlled by the variable
347 `gnus-auto-select-subject'.
349 If you want to prevent automatic selection of articles in some
350 newsgroups, set the variable to nil in `gnus-select-group-hook'."
351 ;; Commands include...
352 ;; \\<gnus-group-mode-map>\\[gnus-group-read-group]
353 ;; \\<gnus-summary-mode-map>\\[gnus-summary-next-page]
354 ;; \\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]
355 :group 'gnus-group-select
356 :type '(choice (const :tag "none" nil)
357 (sexp :menu-tag "first" t)))
359 (defcustom gnus-auto-select-subject 'unseen-or-unread
360 "*Says what subject to place under point when entering a group.
362 This variable can either be the symbols `first' (place point on the
363 first subject), `unread' (place point on the subject line of the first
364 unread article), `best' (place point on the subject line of the
365 highest-scored article), `unseen' (place point on the subject line of
366 the first unseen article), `unseen-or-unread' (place point on the subject
367 line of the first unseen article or, if all articles have been seen, on the
368 subject line of the first unread article), or a function to be called to
369 place point on some subject line."
371 :group 'gnus-group-select
372 :type '(choice (const best)
376 (const unseen-or-unread)
377 (function :tag "Function to call")))
379 (defcustom gnus-auto-select-next t
380 "*If non-nil, offer to go to the next group from the end of the previous.
381 If the value is t and the next newsgroup is empty, Gnus will exit
382 summary mode and go back to group mode. If the value is neither nil
383 nor t, Gnus will select the following unread newsgroup. In
384 particular, if the value is the symbol `quietly', the next unread
385 newsgroup will be selected without any confirmation, and if it is
386 `almost-quietly', the next group will be selected without any
387 confirmation if you are located on the last article in the group.
388 Finally, if this variable is `slightly-quietly', the `\\<gnus-summary-mode-map>\\[gnus-summary-catchup-and-goto-next-group]' command
389 will go to the next group without confirmation."
390 :group 'gnus-summary-maneuvering
391 :type '(choice (const :tag "off" nil)
393 (const almost-quietly)
394 (const slightly-quietly)
395 (sexp :menu-tag "on" t)))
397 (defcustom gnus-auto-select-same nil
398 "*If non-nil, select the next article with the same subject.
399 If there are no more articles with the same subject, go to
400 the first unread article."
401 :group 'gnus-summary-maneuvering
404 (defcustom gnus-auto-select-on-ephemeral-exit 'next-noselect
405 "What article should be selected after exiting an ephemeral group.
406 Valid values include:
409 Select the next article.
411 Select the next unread article.
413 Move the cursor to the next article. This is the default.
414 `next-unread-noselect'
415 Move the cursor to the next unread article.
417 If it has any other value or there is no next (unread) article, the
418 article selected before entering to the ephemeral group will appear."
419 :version "23.1" ;; No Gnus
420 :group 'gnus-summary-maneuvering
421 :type '(choice :format "%{%t%}:\n %[Value Menu%] %v"
422 (const next) (const next-unread)
423 (const next-noselect) (const next-unread-noselect)
424 (sexp :tag "other" :value nil)))
426 (defcustom gnus-auto-goto-ignores 'unfetched
427 "*Says how to handle unfetched articles when maneuvering.
429 This variable can either be the symbols nil (maneuver to any
430 article), `undownloaded' (maneuvering while unplugged ignores articles
431 that have not been fetched), `always-undownloaded' (maneuvering always
432 ignores articles that have not been fetched), `unfetched' (maneuvering
433 ignores articles whose headers have not been fetched).
435 NOTE: The list of unfetched articles will always be nil when plugged
436 and, when unplugged, a subset of the undownloaded article list."
438 :group 'gnus-summary-maneuvering
439 :type '(choice (const :tag "None" nil)
440 (const :tag "Undownloaded when unplugged" undownloaded)
441 (const :tag "Undownloaded" always-undownloaded)
442 (const :tag "Unfetched" unfetched)))
444 (defcustom gnus-summary-check-current nil
445 "*If non-nil, consider the current article when moving.
446 The \"unread\" movement commands will stay on the same line if the
447 current article is unread."
448 :group 'gnus-summary-maneuvering
451 (defcustom gnus-auto-center-summary
452 (max (or (bound-and-true-p scroll-margin) 0) 2)
453 "*If non-nil, always center the current summary buffer.
454 In particular, if `vertical' do only vertical recentering. If non-nil
455 and non-`vertical', do both horizontal and vertical recentering."
456 :group 'gnus-summary-maneuvering
457 :type '(choice (const :tag "none" nil)
459 (integer :tag "height")
460 (sexp :menu-tag "both" t)))
462 (defcustom gnus-auto-center-group t
463 "If non-nil, always center the group buffer."
464 :group 'gnus-summary-maneuvering
467 (defcustom gnus-show-all-headers nil
468 "*If non-nil, don't hide any headers."
469 :group 'gnus-article-hiding
470 :group 'gnus-article-headers
473 (defcustom gnus-summary-ignore-duplicates nil
474 "*If non-nil, ignore articles with identical Message-ID headers."
478 (defcustom gnus-single-article-buffer nil
479 "*If non-nil, display all articles in the same buffer.
480 If nil, each group will get its own article buffer."
482 :group 'gnus-article-various
485 (defcustom gnus-widen-article-window nil
486 "If non-nil, selecting the article buffer will display only the article buffer."
488 :group 'gnus-article-various
491 (defcustom gnus-break-pages t
492 "*If non-nil, do page breaking on articles.
493 The page delimiter is specified by the `gnus-page-delimiter'
495 :group 'gnus-article-various
498 (defcustom gnus-move-split-methods nil
499 "*Variable used to suggest where articles are to be moved to.
500 It uses the same syntax as the `gnus-split-methods' variable.
501 However, whereas `gnus-split-methods' specifies file names as targets,
502 this variable specifies group names."
503 :group 'gnus-summary-mail
504 :type '(repeat (choice (list :value (fun) function)
505 (cons :value ("" "") regexp (repeat string))
508 (defcustom gnus-move-group-prefix-function 'gnus-group-real-prefix
509 "Function used to compute default prefix for article move/copy/etc prompts.
510 The function should take one argument, a group name, and return a
511 string with the suggested prefix."
512 :group 'gnus-summary-mail
515 ;; FIXME: Although the custom type is `character' for the following variables,
516 ;; using multibyte characters (Latin-1, UTF-8) doesn't work. -- rs
518 (defcustom gnus-unread-mark ? ;Whitespace
519 "*Mark used for unread articles."
520 :group 'gnus-summary-marks
523 (defcustom gnus-ticked-mark ?!
524 "*Mark used for ticked articles."
525 :group 'gnus-summary-marks
528 (defcustom gnus-dormant-mark ??
529 "*Mark used for dormant articles."
530 :group 'gnus-summary-marks
533 (defcustom gnus-del-mark ?r
534 "*Mark used for del'd articles."
535 :group 'gnus-summary-marks
538 (defcustom gnus-read-mark ?R
539 "*Mark used for read articles."
540 :group 'gnus-summary-marks
543 (defcustom gnus-expirable-mark ?E
544 "*Mark used for expirable articles."
545 :group 'gnus-summary-marks
548 (defcustom gnus-killed-mark ?K
549 "*Mark used for killed articles."
550 :group 'gnus-summary-marks
553 (defcustom gnus-spam-mark ?$
554 "*Mark used for spam articles."
556 :group 'gnus-summary-marks
559 (defcustom gnus-kill-file-mark ?X
560 "*Mark used for articles killed by kill files."
561 :group 'gnus-summary-marks
564 (defcustom gnus-low-score-mark ?Y
565 "*Mark used for articles with a low score."
566 :group 'gnus-summary-marks
569 (defcustom gnus-catchup-mark ?C
570 "*Mark used for articles that are caught up."
571 :group 'gnus-summary-marks
574 (defcustom gnus-replied-mark ?A
575 "*Mark used for articles that have been replied to."
576 :group 'gnus-summary-marks
579 (defcustom gnus-forwarded-mark ?F
580 "*Mark used for articles that have been forwarded."
582 :group 'gnus-summary-marks
585 (defcustom gnus-recent-mark ?N
586 "*Mark used for articles that are recent."
588 :group 'gnus-summary-marks
591 (defcustom gnus-cached-mark ?*
592 "*Mark used for articles that are in the cache."
593 :group 'gnus-summary-marks
596 (defcustom gnus-saved-mark ?S
597 "*Mark used for articles that have been saved."
598 :group 'gnus-summary-marks
601 (defcustom gnus-unseen-mark ?.
602 "*Mark used for articles that haven't been seen."
604 :group 'gnus-summary-marks
607 (defcustom gnus-no-mark ? ;Whitespace
608 "*Mark used for articles that have no other secondary mark."
610 :group 'gnus-summary-marks
613 (defcustom gnus-ancient-mark ?O
614 "*Mark used for ancient articles."
615 :group 'gnus-summary-marks
618 (defcustom gnus-sparse-mark ?Q
619 "*Mark used for sparsely reffed articles."
620 :group 'gnus-summary-marks
623 (defcustom gnus-canceled-mark ?G
624 "*Mark used for canceled articles."
625 :group 'gnus-summary-marks
628 (defcustom gnus-duplicate-mark ?M
629 "*Mark used for duplicate articles."
630 :group 'gnus-summary-marks
633 (defcustom gnus-undownloaded-mark ?-
634 "*Mark used for articles that weren't downloaded."
636 :group 'gnus-summary-marks
639 (defcustom gnus-downloaded-mark ?+
640 "*Mark used for articles that were downloaded."
641 :group 'gnus-summary-marks
644 (defcustom gnus-downloadable-mark ?%
645 "*Mark used for articles that are to be downloaded."
646 :group 'gnus-summary-marks
649 (defcustom gnus-unsendable-mark ?=
650 "*Mark used for articles that won't be sent."
651 :group 'gnus-summary-marks