gnus-notifications: add actions support
[gnus] / lisp / gnus-cite.el
index 734d21c..d107dfa 100644 (file)
@@ -1,11 +1,15 @@
 ;;; gnus-cite.el --- parse citations in articles for Gnus
 ;;; gnus-cite.el --- parse citations in articles for Gnus
-;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000
-;;        Free Software Foundation, Inc.
 
 
-;; Author: Per Abhiddenware; you can redistribute it and/or modify
+;; Copyright (C) 1995-2012 Free Software Foundation, Inc.
+
+;; Author: Per Abhiddenware
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 ;;; Code:
 
 (eval-when-compile (require 'cl))
 
 ;;; Commentary:
 
 ;;; Code:
 
 (eval-when-compile (require 'cl))
+(eval-when-compile
+  (when (featurep 'xemacs)
+    (require 'easy-mmode))) ; for `define-minor-mode'
 
 (require 'gnus)
 
 (require 'gnus)
-(require 'gnus-art)
 (require 'gnus-range)
 (require 'gnus-range)
+(require 'gnus-art)
 (require 'message)     ; for message-cite-prefix-regexp
 
 ;;; Customization:
 (require 'message)     ; for message-cite-prefix-regexp
 
 ;;; Customization:
   :link '(custom-manual "(gnus)Article Highlighting")
   :group 'gnus-article)
 
   :link '(custom-manual "(gnus)Article Highlighting")
   :group 'gnus-article)
 
-(defcustom gnus-cite-reply-regexp
-  "^\\(Subject: Re\\|In-Reply-To\\|References\\):"
-  "*If headers match this regexp it is reasonable to believe that
-article has citations."
-  :group 'gnus-cite
-  :type 'string)
-
-(defcustom gnus-cite-always-check nil
-  "Check article always for citations.  Set it t to check all articles."
-  :group 'gnus-cite
-  :type '(choice (const :tag "no" nil)
-                (const :tag "yes" t)))
-
 (defcustom gnus-cited-opened-text-button-line-format "%(%{[-]%}%)\n"
   "Format of opened cited text buttons."
   :group 'gnus-cite
 (defcustom gnus-cited-opened-text-button-line-format "%(%{[-]%}%)\n"
   "Format of opened cited text buttons."
   :group 'gnus-cite
@@ -99,150 +91,243 @@ The first regexp group should match the Supercite attribution."
   :group 'gnus-cite
   :type 'integer)
 
   :group 'gnus-cite
   :type 'integer)
 
+;; Some Microsoft products put in a citation that extends to the
+;; remainder of the message:
+;;
+;;     -----Original Message-----
+;;     From: ...
+;;     To: ...
+;;     Sent: ...   [date, in non-RFC-2822 format]
+;;     Subject: ...
+;;
+;;     Cited message, with no prefixes
+;;
+;; The four headers are always the same.  But note they are prone to
+;; folding without additional indentation.
+;;
+;; Others use "----- Original Message -----" instead, and properly quote
+;; the body using "> ".  This style is handled without special cases.
+
 (defcustom gnus-cite-attribution-prefix
 (defcustom gnus-cite-attribution-prefix
-  "In article\\|in <\\|On \\(Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\),\\|-----Original Message-----"
+  "In article\\|in <\\|On \\(Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\),\\|----- ?Original Message ?-----"
   "*Regexp matching the beginning of an attribution line."
   :group 'gnus-cite
   :type 'regexp)
 
 (defcustom gnus-cite-attribution-suffix
   "*Regexp matching the beginning of an attribution line."
   :group 'gnus-cite
   :type 'regexp)
 
 (defcustom gnus-cite-attribution-suffix
-  "\\(\\(wrote\\|writes\\|said\\|says\\|>\\)\\(:\\|\\.\\.\\.\\)\\|-----Original Message-----\\)[ \t]*$"
+  "\\(\\(wrote\\|writes\\|said\\|says\\|>\\)\\(:\\|\\.\\.\\.\\)\\|----- ?Original Message ?-----\\)[ \t]*$"
   "*Regexp matching the end of an attribution line.
 The text matching the first grouping will be used as a button."
   :group 'gnus-cite
   :type 'regexp)
 
   "*Regexp matching the end of an attribution line.
 The text matching the first grouping will be used as a button."
   :group 'gnus-cite
   :type 'regexp)
 
-(defface gnus-cite-attribution-face '((t
-                                      (:italic t)))
-  "Face used for attribution lines.")
+(defcustom gnus-cite-unsightly-citation-regexp
+  "^-----Original Message-----\nFrom: \\(.+\n\\)+\n"
+  "Regexp matching Microsoft-type rest-of-message citations."
+  :version "22.1"
+  :group 'gnus-cite
+  :type 'regexp)
 
 
-(defcustom gnus-cite-attribution-face 'gnus-cite-attribution-face
+(defcustom gnus-cite-ignore-quoted-from t
+  "Non-nil means don't regard lines beginning with \">From \" as cited text.
+Those lines may have been quoted by MTAs in order not to mix up with
+the envelope From line."
+  :version "22.1"
+  :group 'gnus-cite
+  :type 'boolean)
+
+(defface gnus-cite-attribution '((t (:italic t)))
+  "Face used for attribution lines."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-attribution-face 'face-alias 'gnus-cite-attribution)
+(put 'gnus-cite-attribution-face 'obsolete-face "22.1")
+
+(defcustom gnus-cite-attribution-face 'gnus-cite-attribution
   "Face used for attribution lines.
 It is merged with the face for the cited text belonging to the attribution."
   "Face used for attribution lines.
 It is merged with the face for the cited text belonging to the attribution."
+  :version "22.1"
   :group 'gnus-cite
   :type 'face)
 
   :group 'gnus-cite
   :type 'face)
 
-(defface gnus-cite-face-1 '((((class color)
-                             (background dark))
-                            (:foreground "light blue"))
-                           (((class color)
-                             (background light))
-                            (:foreground "MidnightBlue"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-2 '((((class color)
-                             (background dark))
-                            (:foreground "light cyan"))
-                           (((class color)
-                             (background light))
-                            (:foreground "firebrick"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-3 '((((class color)
-                             (background dark))
-                            (:foreground "light yellow"))
-                           (((class color)
-                             (background light))
-                            (:foreground "dark green"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-4 '((((class color)
-                             (background dark))
-                            (:foreground "light pink"))
-                           (((class color)
-                             (background light))
-                            (:foreground "OrangeRed"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-5 '((((class color)
-                             (background dark))
-                            (:foreground "pale green"))
-                           (((class color)
-                             (background light))
-                            (:foreground "dark khaki"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-6 '((((class color)
-                             (background dark))
-                            (:foreground "beige"))
-                           (((class color)
-                             (background light))
-                            (:foreground "dark violet"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-7 '((((class color)
-                             (background dark))
-                            (:foreground "orange"))
-                           (((class color)
-                             (background light))
-                            (:foreground "SteelBlue4"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-8 '((((class color)
-                             (background dark))
-                            (:foreground "magenta"))
-                           (((class color)
-                             (background light))
-                            (:foreground "magenta"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-9 '((((class color)
-                             (background dark))
-                            (:foreground "violet"))
-                           (((class color)
-                             (background light))
-                            (:foreground "violet"))
-                           (t
-                            (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-10 '((((class color)
-                              (background dark))
-                             (:foreground "medium purple"))
-                            (((class color)
-                              (background light))
-                             (:foreground "medium purple"))
-                            (t
-                             (:italic t)))
-  "Citation face.")
-
-(defface gnus-cite-face-11 '((((class color)
-                              (background dark))
-                             (:foreground "turquoise"))
-                            (((class color)
-                              (background light))
-                             (:foreground "turquoise"))
-                            (t
-                             (:italic t)))
-  "Citation face.")
+(defface gnus-cite-1 '((((class color)
+                        (background dark))
+                       (:foreground "light blue"))
+                      (((class color)
+                        (background light))
+                       (:foreground "MidnightBlue"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-1 'face-alias 'gnus-cite-1)
+(put 'gnus-cite-face-1 'obsolete-face "22.1")
+
+(defface gnus-cite-2 '((((class color)
+                        (background dark))
+                       (:foreground "light cyan"))
+                      (((class color)
+                        (background light))
+                       (:foreground "firebrick"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-2 'face-alias 'gnus-cite-2)
+(put 'gnus-cite-face-2 'obsolete-face "22.1")
+
+(defface gnus-cite-3 '((((class color)
+                        (background dark))
+                       (:foreground "light yellow"))
+                      (((class color)
+                        (background light))
+                       (:foreground "dark green"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-3 'face-alias 'gnus-cite-3)
+(put 'gnus-cite-face-3 'obsolete-face "22.1")
+
+(defface gnus-cite-4 '((((class color)
+                        (background dark))
+                       (:foreground "light pink"))
+                      (((class color)
+                        (background light))
+                       (:foreground "OrangeRed"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-4 'face-alias 'gnus-cite-4)
+(put 'gnus-cite-face-4 'obsolete-face "22.1")
+
+(defface gnus-cite-5 '((((class color)
+                        (background dark))
+                       (:foreground "pale green"))
+                      (((class color)
+                        (background light))
+                       (:foreground "dark khaki"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-5 'face-alias 'gnus-cite-5)
+(put 'gnus-cite-face-5 'obsolete-face "22.1")
+
+(defface gnus-cite-6 '((((class color)
+                        (background dark))
+                       (:foreground "beige"))
+                      (((class color)
+                        (background light))
+                       (:foreground "dark violet"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-6 'face-alias 'gnus-cite-6)
+(put 'gnus-cite-face-6 'obsolete-face "22.1")
+
+(defface gnus-cite-7 '((((class color)
+                        (background dark))
+                       (:foreground "orange"))
+                      (((class color)
+                        (background light))
+                       (:foreground "SteelBlue4"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-7 'face-alias 'gnus-cite-7)
+(put 'gnus-cite-face-7 'obsolete-face "22.1")
+
+(defface gnus-cite-8 '((((class color)
+                        (background dark))
+                       (:foreground "magenta"))
+                      (((class color)
+                        (background light))
+                       (:foreground "magenta"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-8 'face-alias 'gnus-cite-8)
+(put 'gnus-cite-face-8 'obsolete-face "22.1")
+
+(defface gnus-cite-9 '((((class color)
+                        (background dark))
+                       (:foreground "violet"))
+                      (((class color)
+                        (background light))
+                       (:foreground "violet"))
+                      (t
+                       (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-9 'face-alias 'gnus-cite-9)
+(put 'gnus-cite-face-9 'obsolete-face "22.1")
+
+(defface gnus-cite-10 '((((class color)
+                         (background dark))
+                        (:foreground "plum1"))
+                       (((class color)
+                         (background light))
+                        (:foreground "medium purple"))
+                       (t
+                        (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-10 'face-alias 'gnus-cite-10)
+(put 'gnus-cite-face-10 'obsolete-face "22.1")
+
+(defface gnus-cite-11 '((((class color)
+                         (background dark))
+                        (:foreground "turquoise"))
+                       (((class color)
+                         (background light))
+                        (:foreground "turquoise"))
+                       (t
+                        (:italic t)))
+  "Citation face."
+  :group 'gnus-cite)
+;; backward-compatibility alias
+(put 'gnus-cite-face-11 'face-alias 'gnus-cite-11)
+(put 'gnus-cite-face-11 'obsolete-face "22.1")
 
 (defcustom gnus-cite-face-list
 
 (defcustom gnus-cite-face-list
-  '(gnus-cite-face-1 gnus-cite-face-2 gnus-cite-face-3 gnus-cite-face-4
-                    gnus-cite-face-5 gnus-cite-face-6 gnus-cite-face-7 gnus-cite-face-8
-                    gnus-cite-face-9 gnus-cite-face-10 gnus-cite-face-11)
+  '(gnus-cite-1 gnus-cite-2 gnus-cite-3 gnus-cite-4 gnus-cite-5 gnus-cite-6
+               gnus-cite-7 gnus-cite-8 gnus-cite-9 gnus-cite-10 gnus-cite-11)
   "*List of faces used for highlighting citations.
 
 When there are citations from multiple articles in the same message,
 Gnus will try to give each citation from each article its own face.
 This should make it easier to see who wrote what."
   :group 'gnus-cite
   "*List of faces used for highlighting citations.
 
 When there are citations from multiple articles in the same message,
 Gnus will try to give each citation from each article its own face.
 This should make it easier to see who wrote what."
   :group 'gnus-cite
-  :type '(repeat face))
+  :type '(repeat face)
+  :set (lambda (symbol value)
+        (prog1
+            (custom-set-default symbol value)
+          (if (boundp 'gnus-message-max-citation-depth)
+              (setq gnus-message-max-citation-depth (length value)))
+          (if (boundp 'gnus-message-citation-keywords)
+              (setq gnus-message-citation-keywords
+                    `((gnus-message-search-citation-line
+                       ,@(let ((list nil)
+                               (count 1))
+                           (dolist (face value (nreverse list))
+                             (push (list count (list 'quote face) 'prepend t)
+                                   list)
+                             (setq count (1+ count)))))))))))
 
 (defcustom gnus-cite-hide-percentage 50
   "Only hide excess citation if above this percentage of the body."
 
 (defcustom gnus-cite-hide-percentage 50
   "Only hide excess citation if above this percentage of the body."
@@ -254,6 +339,21 @@ This should make it easier to see who wrote what."
   :group 'gnus-cite
   :type 'integer)
 
   :group 'gnus-cite
   :type 'integer)
 
+(defcustom gnus-cite-blank-line-after-header t
+  "If non-nil, put a blank line between the citation header and the button."
+  :group 'gnus-cite
+  :type 'boolean)
+
+;; This has to go here because its default value depends on
+;; gnus-cite-face-list.
+(defcustom gnus-article-boring-faces (cons 'gnus-signature gnus-cite-face-list)
+  "List of faces that are not worth reading.
+If an article has more pages below the one you are looking at, but
+nothing on those pages is a word of at least three letters that is not
+in a boring face, then the pages will be skipped."
+  :type '(repeat face)
+  :group 'gnus-article-hiding)
+
 ;;; Internal Variables:
 
 (defvar gnus-cite-article nil)
 ;;; Internal Variables:
 
 (defvar gnus-cite-article nil)
@@ -293,12 +393,12 @@ This should make it easier to see who wrote what."
 
 ;;; Commands:
 
 
 ;;; Commands:
 
-(defun gnus-article-highlight-citation (&optional force)
+(defun gnus-article-highlight-citation (&optional force same-buffer)
   "Highlight cited text.
 Each citation in the article will be highlighted with a different face.
 The faces are taken from `gnus-cite-face-list'.
 Attribution lines are highlighted with the same face as the
   "Highlight cited text.
 Each citation in the article will be highlighted with a different face.
 The faces are taken from `gnus-cite-face-list'.
 Attribution lines are highlighted with the same face as the
-corresponding citation merged with `gnus-cite-attribution-face'.
+corresponding citation merged with the face `gnus-cite-attribution'.
 
 Text is considered cited if at least `gnus-cite-minimum-match-count'
 lines matches `message-cite-prefix-regexp' with the same prefix.
 
 Text is considered cited if at least `gnus-cite-minimum-match-count'
 lines matches `message-cite-prefix-regexp' with the same prefix.
@@ -306,8 +406,7 @@ lines matches `message-cite-prefix-regexp' with the same prefix.
 Lines matching `gnus-cite-attribution-suffix' and perhaps
 `gnus-cite-attribution-prefix' are considered attribution lines."
   (interactive (list 'force))
 Lines matching `gnus-cite-attribution-suffix' and perhaps
 `gnus-cite-attribution-prefix' are considered attribution lines."
   (interactive (list 'force))
-  (save-excursion
-    (set-buffer gnus-article-buffer)
+  (with-current-buffer (if same-buffer (current-buffer) gnus-article-buffer)
     (gnus-cite-parse-maybe force)
     (let ((buffer-read-only nil)
          (alist gnus-cite-prefix-alist)
     (gnus-cite-parse-maybe force)
     (let ((buffer-read-only nil)
          (alist gnus-cite-prefix-alist)
@@ -342,7 +441,7 @@ Lines matching `gnus-cite-attribution-suffix' and perhaps
        (goto-char (point-min))
        (forward-line (1- number))
        (when (re-search-forward gnus-cite-attribution-suffix
        (goto-char (point-min))
        (forward-line (1- number))
        (when (re-search-forward gnus-cite-attribution-suffix
-                                (save-excursion (end-of-line 1) (point))
+                                (point-at-eol)
                                 t)
          (gnus-article-add-button (match-beginning 1) (match-end 1)
                                   'gnus-cite-toggle prefix))
                                 t)
          (gnus-article-add-button (match-beginning 1) (match-end 1)
                                   'gnus-cite-toggle prefix))
@@ -360,8 +459,7 @@ Lines matching `gnus-cite-attribution-suffix' and perhaps
 
 (defun gnus-dissect-cited-text ()
   "Dissect the article buffer looking for cited text."
 
 (defun gnus-dissect-cited-text ()
   "Dissect the article buffer looking for cited text."
-  (save-excursion
-    (set-buffer gnus-article-buffer)
+  (with-current-buffer gnus-article-buffer
     (gnus-cite-parse-maybe nil t)
     (let ((alist gnus-cite-prefix-alist)
          prefix numbers number marks m)
     (gnus-cite-parse-maybe nil t)
     (let ((alist gnus-cite-prefix-alist)
          prefix numbers number marks m)
@@ -411,18 +509,23 @@ Lines matching `gnus-cite-attribution-suffix' and perhaps
          (if (and (equal (cdadr m) "")
                   (equal (cdar m) (cdaddr m))
                   (goto-char (caadr m))
          (if (and (equal (cdadr m) "")
                   (equal (cdar m) (cdaddr m))
                   (goto-char (caadr m))
+                  (looking-at "[ \t]*$")
                   (forward-line 1)
                   (= (point) (caaddr m)))
              (setcdr m (cdddr m))
            (setq m (cdr m))))
        marks))))
 
                   (forward-line 1)
                   (= (point) (caaddr m)))
              (setcdr m (cdddr m))
            (setq m (cdr m))))
        marks))))
 
-(defun gnus-article-fill-cited-article (&optional force width)
+(defun gnus-article-fill-cited-long-lines ()
+  (gnus-article-fill-cited-article nil t))
+
+(defun gnus-article-fill-cited-article (&optional width long-lines)
   "Do word wrapping in the current article.
   "Do word wrapping in the current article.
-If WIDTH (the numerical prefix), use that text width when filling."
-  (interactive (list t current-prefix-arg))
-  (save-excursion
-    (set-buffer gnus-article-buffer)
+If WIDTH (the numerical prefix), use that text width when
+filling.  If LONG-LINES, only fill sections that have lines
+longer than the frame width."
+  (interactive "P")
+  (with-current-buffer gnus-article-buffer
<