;;; gnus-cite.el --- parse citations in articles for Gnus
-;; Copyright (C) 1995,96,97,98 Free Software Foundation, Inc.
-;; Author: Per Abrahamsen <abraham@iesd.auc.dk>
-;; Keywords: news, mail
+;; Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
+;; 2004, 2005, 2006 Free Software Foundation, Inc.
+
+;; Author: Per Abhiddenware
;; This file is part of GNU Emacs.
;; 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.
+;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+;; Boston, MA 02110-1301, USA.
;;; Commentary:
;;; Code:
(eval-when-compile (require 'cl))
+(eval-when-compile
+ (when (featurep 'xemacs)
+ (require 'easy-mmode))) ; for `define-minor-mode'
(require 'gnus)
-(require 'gnus-art)
(require 'gnus-range)
+(require 'gnus-art)
+(require 'message) ; for message-cite-prefix-regexp
;;; Customization:
: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."
+(defcustom gnus-cited-opened-text-button-line-format "%(%{[-]%}%)\n"
+ "Format of opened cited text buttons."
: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-text-button-line-format "%(%{[...]%}%)\n"
- "Format of cited text buttons."
+(defcustom gnus-cited-closed-text-button-line-format "%(%{[+]%}%)\n"
+ "Format of closed cited text buttons."
:group 'gnus-cite
:type 'string)
(defcustom gnus-cited-lines-visible nil
- "The number of lines of hidden cited text to remain visible."
+ "The number of lines of hidden cited text to remain visible.
+Or a pair (cons) of numbers which are the number of lines at the top
+and bottom of the text, respectively, to remain visible."
:group 'gnus-cite
:type '(choice (const :tag "none" nil)
- integer))
+ integer
+ (cons :tag "Top and Bottom" integer integer)))
(defcustom gnus-cite-parse-max-size 25000
"Maximum article size (in bytes) where parsing citations is allowed.
:type '(choice (const :tag "all" nil)
integer))
-(defcustom gnus-cite-prefix-regexp
- "^[]>|:}+ ]*[]>|:}+]\\(.*>\\)?\\|^.*>"
- "*Regexp matching the longest possible citation prefix on a line."
- :group 'gnus-cite
- :type 'regexp)
-
(defcustom gnus-cite-max-prefix 20
"Maximum possible length for a citation prefix."
:group 'gnus-cite
:type 'integer)
(defcustom gnus-supercite-regexp
- (concat "^\\(" gnus-cite-prefix-regexp "\\)? *"
+ (concat "^\\(" message-cite-prefix-regexp "\\)? *"
">>>>> +\"\\([^\"\n]+\\)\" +==")
"*Regexp matching normal Supercite attribution lines.
The first grouping must match prefixes added by other packages."
:group 'gnus-cite
:type 'integer)
-(defcustom gnus-cite-attribution-prefix
- "In article\\|in <\\|On \\(Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\),"
+;; 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
+ "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
- "\\(\\(wrote\\|writes\\|said\\|says\\|>\\)\\(:\\|\\.\\.\\.\\)\\)[ \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)
-(defface gnus-cite-attribution-face '((t
- (:underline 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-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)
-(defcustom gnus-cite-attribution-face 'gnus-cite-attribution-face
+(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."
+ :version "22.1"
: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)
+
+(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)
+
+(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)
+
+(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)
+
+(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)
+
+(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)
+
+(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)
+
+(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)
+
+(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)
+
+(defface gnus-cite-10 '((((class color)
+ (background dark))
+ (:foreground "medium purple"))
+ (((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)
+
+(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)
(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,
: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)
;; PREFIX: Is the citation prefix of the attribution line(s), and
;; TAG: Is a Supercite tag, if any.
-(defvar gnus-cited-text-button-line-format-alist
+(defvar gnus-cited-opened-text-button-line-format-alist
`((?b (marker-position beg) ?d)
(?e (marker-position end) ?d)
+ (?n (count-lines beg end) ?d)
(?l (- end beg) ?d)))
-(defvar gnus-cited-text-button-line-format-spec nil)
+(defvar gnus-cited-opened-text-button-line-format-spec nil)
+(defvar gnus-cited-closed-text-button-line-format-alist
+ gnus-cited-opened-text-button-line-format-alist)
+(defvar gnus-cited-closed-text-button-line-format-spec nil)
+
;;; Commands:
-(defun gnus-article-highlight-citation (&optional force)
+(defun gnus-article-highlight-citation (&optional force keep-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
-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 `gnus-cite-prefix-regexp' with the same prefix.
+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))
(save-excursion
- (set-buffer gnus-article-buffer)
+ (unless keep-buffer
+ (set-buffer gnus-article-buffer))
(gnus-cite-parse-maybe force)
(let ((buffer-read-only nil)
(alist gnus-cite-prefix-alist)
skip (gnus-cite-find-prefix number)
face (cdr (assoc prefix face-alist)))
;; Add attribution button.
- (goto-line number)
+ (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))
"Dissect the article buffer looking for cited text."
(save-excursion
(set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe)
+ (gnus-cite-parse-maybe nil t)
(let ((alist gnus-cite-prefix-alist)
prefix numbers number marks m)
;; Loop through citation prefixes.
(forward-line (1- number))
(push (cons (point-marker) prefix) marks)))
;; Skip to the beginning of the body.
- (goto-char (point-min))
- (search-forward "\n\n" nil t)
+ (article-goto-body)
(push (cons (point-marker) "") marks)
;; Find the end of the body.
(goto-char (point-max))
(fill-column (if width (prefix-numeric-value width) fill-column)))
(save-restriction
(while (cdr marks)
- (widen)
(narrow-to-region (caar marks) (caadr marks))
(let ((adaptive-fill-regexp
(concat "^" (regexp-quote (cdar marks)) " *"))
- (fill-prefix (cdar marks)))
+ (fill-prefix
+ (if (string= (cdar marks) "") ""
+ (concat (cdar marks) " ")))
+ use-hard-newlines)
(fill-region (point-min) (point-max)))
(set-marker (caar marks) nil)
(setq marks (cdr marks)))
If given a negative prefix, always show; if given a positive prefix,
always hide."
(interactive (append (gnus-article-hidden-arg) (list 'force)))
- (gnus-set-format 'cited-text-button t)
+ (gnus-set-format 'cited-opened-text-button t)
+ (gnus-set-format 'cited-closed-text-button t)
(save-excursion
(set-buffer gnus-article-buffer)
- (cond
- ((gnus-article-check-hidden-text 'cite arg)
- t)
- ((gnus-article-text-type-exists-p 'cite)
- (let ((buffer-read-only nil))
- (gnus-article-hide-text-of-type 'cite)))
- (t
(let ((buffer-read-only nil)
- (marks (gnus-dissect-cited-text))
+ marks
(inhibit-point-motion-hooks t)
(props (nconc (list 'article-type 'cite)
gnus-hidden-properties))
- beg end)
- (while marks
- (setq beg nil
- end nil)
- (while (and marks (string= (cdar marks) ""))
- (setq marks (cdr marks)))
- (when marks
- (setq beg (caar marks)))
- (while (and marks (not (string= (cdar marks) "")))
- (setq marks (cdr marks)))
- (when marks
+ (point (point-min))
+ found beg end start)
+ (while (setq point
+ (text-property-any point (point-max)
+ 'gnus-callback
+ 'gnus-article-toggle-cited-text))
+ (setq found t)
+ (goto-char point)
+ (gnus-article-toggle-cited-text
+ (get-text-property point 'gnus-data) arg)
+ (forward-line 1)
+ (setq point (point)))
+ (unless found
+ (setq marks (gnus-dissect-cited-text))
+ (while marks
+ (setq beg nil
+ end nil)
+ (while (and marks (string= (cdar marks) ""))
+ (setq marks (cdr marks)))
+ (when marks
+ (setq beg (caar marks)))
+ (while (and marks (not (string= (cdar marks) "")))
+ (setq marks (cdr marks)))
+ (when marks
(setq end (caar marks)))
- ;; Skip past lines we want to leave visible.
- (when (and beg end gnus-cited-lines-visible)
- (goto-char beg)
- (forward-line gnus-cited-lines-visible)
- (if (>= (point) end)
- (setq beg nil)
- (setq beg (point-marker))))
- (when (and beg end)
- (gnus-add-text-properties beg end props)
- (goto-char beg)
- (unless (save-excursion (search-backward "\n\n" nil t))
- (insert "\n"))
- (put-text-property
- (point)
- (progn
+ ;; Skip past lines we want to leave visible.
+ (when (and beg end gnus-cited-lines-visible)
+ (goto-char beg)
+ (forward-line (if (consp gnus-cited-lines-visible)
+ (car gnus-cited-lines-visible)
+ gnus-cited-lines-visible))
+ (if (>= (point) end)
+ (setq beg nil)
+ (setq beg (point-marker))
+ (when (consp gnus-cited-lines-visible)
+ (goto-char end)
+ (forward-line (- (cdr gnus-cited-lines-visible)))
+ (if (<= (point) beg)
+ (setq beg nil)
+ (setq end (point-marker))))))
+ (when (and beg end)
+ (gnus-add-wash-type 'cite)
+ ;; We use markers for the end-points to facilitate later
+ ;; wrapping and mangling of text.
+ (setq beg (set-marker (make-marker) beg)
+ end (set-marker (make-marker) end))
+ (gnus-add-text-properties-when 'article-type nil beg end props)
+ (goto-char beg)
+ (when (and gnus-cite-blank-line-after-header
+ (not (save-excursion (search-backward "\n\n" nil t))))
+ (insert "\n"))
+ (put-text-property
+ (setq start (point-marker))
+ (progn
(gnus-article-add-button
(point)
- (progn (eval gnus-cited-text-button-line-format-spec) (point))
+ (progn (eval gnus-cited-closed-text-button-line-format-spec)
+ (point))
`gnus-article-toggle-cited-text
- ;; We use markers for the end-points to facilitate later
- ;; wrapping and mangling of text.
- (cons (set-marker (make-marker) beg)
- (set-marker (make-marker) end)))
+ (list (cons beg end) start))
(point))
- 'article-type 'annotation)
- (set-marker beg (point)))))))))
-
-(defun gnus-article-toggle-cited-text (region)
- "Toggle hiding the text in REGION."
- (let (buffer-read-only)
- (funcall
- (if (text-property-any
- (car region) (1- (cdr region))
- (car gnus-hidden-properties) (cadr gnus-hidden-properties))
- 'remove-text-properties 'gnus-add-text-properties)
- (car region) (cdr region) gnus-hidden-properties)))
+ 'article-type 'annotation)
+ (set-marker beg (point))))))))
+
+(defun gnus-article-toggle-cited-text (args &optional arg)
+ "Toggle hiding the text in REGION.
+ARG can be nil or a number. Positive means hide, negative
+means show, nil means toggle."
+ (let* ((region (car args))
+ (beg (car region))
+ (end (cdr region))
+ (start (cadr args))
+ (hidden
+ (text-property-any beg (1- end) 'article-type 'cite))
+ (inhibit-point-motion-hooks t)
+ buffer-read-only)
+ (when (or (null arg)
+ (zerop arg)
+ (and (> arg 0) (not hidden))
+ (and (< arg 0) hidden))
+ (if hidden
+ (progn
+ ;; Can't remove 'cite from g-a-wash-types here because
+ ;; multiple citations may be hidden -jas
+ (gnus-remove-text-properties-when
+ 'article-type 'cite beg end
+ (cons 'article-type (cons 'cite
+ gnus-hidden-properties))))
+ (gnus-add-wash-type 'cite)
+ (gnus-add-text-properties-when
+ 'article-type nil beg end
+ (cons 'article-type (cons 'cite
+ gnus-hidden-properties))))
+ (let ((gnus-article-mime-handle-alist-1 gnus-article-mime-handle-alist))
+ (gnus-set-mode-line 'article))
+ (save-excursion
+ (goto-char start)
+ (gnus-delete-line)
+ (put-text-property
+ (point)
+ (progn
+ (gnus-article-add-button
+ (point)
+ (progn (eval
+ (if hidden
+ gnus-cited-opened-text-button-line-format-spec
+ gnus-cited-closed-text-button-line-format-spec))
+ (point))
+ `gnus-article-toggle-cited-text
+ args)
+ (point))
+ 'article-type 'annotation)))))
(defun gnus-article-hide-citation-maybe (&optional arg force)
"Toggle hiding of cited text that has an attribution line.
variables are ignored.
See also the documentation for `gnus-article-highlight-citation'."
(interactive (append (gnus-article-hidden-arg) '(force)))
- (unless (gnus-article-check-hidden-text 'cite arg)
- (save-excursion
- (set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe force)
- (goto-char (point-min))
- (search-forward "\n\n" nil t)
- (let ((start (point))
- (atts gnus-cite-attribution-alist)
- (buffer-read-only nil)
- (inhibit-point-motion-hooks t)
- (hiden 0)
- total)
- (goto-char (point-max))
- (gnus-article-search-signature)
- (setq total (count-lines start (point)))
- (while atts
- (setq hiden (+ hiden (length (cdr (assoc (cdar atts)
- gnus-cite-prefix-alist))))
- atts (cdr atts)))
- (when (or force
- (and (> (* 100 hiden) (* gnus-cite-hide-percentage total))
- (> hiden gnus-cite-hide-absolute)))
- (setq atts gnus-cite-attribution-alist)
+ (with-current-buffer gnus-article-buffer
+ (gnus-delete-wash-type 'cite)
+ (unless (gnus-article-check-hidden-text 'cite arg)
+ (save-excursion
+ (gnus-cite-parse-maybe force)
+ (article-goto-body)
+ (let ((start (point))
+ (atts gnus-cite-attribution-alist)
+ (buffer-read-only nil)
+ (inhibit-point-motion-hooks t)
+ (hidden 0)
+ total)
+ (goto-char (point-max))
+ (gnus-article-search-signature)
+ (setq total (count-lines start (point)))
(while atts
- (setq total (cdr (assoc (cdar atts) gnus-cite-prefix-alist))
- atts (cdr atts))
- (while total
- (setq hiden (car total)
- total (cdr total))
- (goto-line hiden)
- (unless (assq hiden gnus-cite-attribution-alist)
- (gnus-add-text-properties
- (point) (progn (forward-line 1) (point))
- (nconc (list 'article-type 'cite)
- gnus-hidden-properties))))))))))
+ (setq hidden (+ hidden (length (cdr (assoc (cdar atts)
+ gnus-cite-prefix-alist))))
+ atts (cdr atts)))
+ (when (or force
+ (and (> (* 100 hidden) (* gnus-cite-hide-percentage total))
+ (> hidden gnus-cite-hide-absolute)))
+ (gnus-add-wash-type 'cite)
+ (setq atts gnus-cite-attribution-alist)
+ (while atts
+ (setq total (cdr (assoc (cdar atts) gnus-cite-prefix-alist))
+ atts (cdr atts))
+ (while total
+ (setq hidden (car total)
+ total (cdr total))
+ (goto-char (point-min))
+ (forward-line (1- hidden))
+ (unless (assq hidden gnus-cite-attribution-alist)
+ (gnus-add-text-properties
+ (point) (progn (forward-line 1) (point))
+ (nconc (list 'article-type 'cite)
+ gnus-hidden-properties)))))))))
+ (gnus-set-mode-line 'article)))
(defun gnus-article-hide-citation-in-followups ()
"Hide cited text in non-root articles."
;;; Internal functions:
-(defun gnus-cite-parse-maybe (&optional force)
- ;; Parse if the buffer has changes since last time.
- (if (and (not force)
- (equal gnus-cite-article gnus-article-current))
+(defun gnus-cite-parse-maybe (&optional force no-overlay)
+ "Always parse the buffer."
+ (gnus-cite-localize)
+ ;;Reset parser information.
+ (setq gnus-cite-prefix-alist nil
+ gnus-cite-attribution-alist nil
+ gnus-cite-loose-prefix-alist nil
+ gnus-cite-loose-attribution-alist nil)
+ (unless no-overlay
+ (gnus-cite-delete-overlays))
+ ;; Parse if not too large.
+ (if (and gnus-cite-parse-max-size
+ (> (buffer-size) gnus-cite-parse-max-size))
()
- (gnus-cite-localize)
- ;;Reset parser information.
- (setq gnus-cite-prefix-alist nil
- gnus-cite-attribution-alist nil
- gnus-cite-loose-prefix-alist nil
- gnus-cite-loose-attribution-alist nil)
- (while gnus-cite-overlay-list
- (gnus-delete-overlay (pop gnus-cite-overlay-list)))
- ;; Parse if not too large.
- (if (and (not force)
- gnus-cite-parse-max-size
- (> (buffer-size) gnus-cite-parse-max-size))
- ()
- (setq gnus-cite-article (cons (car gnus-article-current)
- (cdr gnus-article-current)))
- (gnus-cite-parse-wrapper))))
+ (setq gnus-cite-article (cons (car gnus-article-current)
+ (cdr gnus-article-current)))
+ (gnus-cite-parse-wrapper)))
+
+(defun gnus-cite-delete-overlays ()
+ (dolist (overlay gnus-cite-overlay-list)
+ (ignore-errors
+ (when (or (not (gnus-overlay-end overlay))
+ (and (>= (gnus-overlay-end overlay) (point-min))
+ (<= (gnus-overlay-end overlay) (point-max))))
+ (setq gnus-cite-overlay-list (delete overlay gnus-cite-overlay-list))
+ (ignore-errors
+ (gnus-delete-overlay overlay))))))
(defun gnus-cite-parse-wrapper ()
- ;; Wrap chopped gnus-cite-parse
- (goto-char (point-min))
- (unless (search-forward "\n\n" nil t)
- (goto-char (point-max)))
- (save-excursion
- (gnus-cite-parse-attributions))
- ;; Try to avoid check citation if there is no reason to believe
- ;; that article has citations
- (if (or gnus-cite-always-check
- (save-excursion
- (re-search-backward gnus-cite-reply-regexp nil t))
- gnus-cite-loose-attribution-alist)
- (progn (save-excursion
- (gnus-cite-parse))
- (save-excursion
- (gnus-cite-connect-attributions)))))
+ ;; Wrap chopped gnus-cite-parse.
+ (article-goto-body)
+ (let ((inhibit-point-motion-hooks t))
+ (save-excursion
+ (gnus-cite-parse-attributions))
+ (save-excursion
+ (gnus-cite-parse))
+ (save-excursion
+ (gnus-cite-connect-attributions))))
(defun gnus-cite-parse ()
;; Parse and connect citation prefixes and attribution lines.
(goto-char (point-max))
(gnus-article-search-signature)
(point)))
- alist entry start begin end numbers prefix)
+ (prefix-regexp (concat "^\\(" message-cite-prefix-regexp "\\)"))
+ alist entry start begin end numbers prefix guess-limit)
;; Get all potential prefixes in `alist'.
(while (< (point) max)
;; Each line.
(setq begin (point)
- end (progn (beginning-of-line 2) (point))
+ guess-limit (progn (skip-chars-forward "^> \t\r\n") (point))
+ end (point-at-bol 2)
start end)
(goto-char begin)
;; Ignore standard Supercite attribution prefix.
- (when (looking-at gnus-supercite-regexp)
+ (when (and (< guess-limit (+ begin gnus-cite-max-prefix))
+ (looking-at gnus-supercite-regexp))
(if (match-end 1)
(setq end (1+ (match-end 1)))
(setq end (1+ begin))))
;; Ignore very long prefixes.
- (when (> end (+ (point) gnus-cite-max-prefix))
- (setq end (+ (point) gnus-cite-max-prefix)))
- (while (re-search-forward gnus-cite-prefix-regexp (1- end) t)
+ (when (> end (+ begin gnus-cite-max-prefix))
+ (setq end (+ begin gnus-cite-max-prefix)))
+ ;; Ignore quoted envelope From_.
+ (when (and gnus-cite-ignore-quoted-from
+ (prog2
+ (setq case-fold-search nil)
+ (looking-at ">From ")
+ (setq case-fold-search t)))
+ (setq end (1+ begin)))
+ (while (re-search-forward prefix-regexp (1- end) t)
;; Each prefix.
(setq end (match-end 0)
prefix (buffer-substring begin end))
- (gnus-set-text-properties 0 (length prefix) nil prefix)
+ (set-text-properties 0 (length prefix) nil prefix)
(setq entry (assoc prefix alist))
(if entry
(setcdr entry (cons line (cdr entry)))
(goto-char begin))
(goto-char start)
(setq line (1+ line)))
+ ;; Horrible special case for some Microsoft mailers.
+ (goto-char (point-min))
+ (when (re-search-forward gnus-cite-unsightly-citation-regexp max t)
+ (setq begin (count-lines (point-min) (point)))
+ (setq end (count-lines (point-min) max))
+ (setq entry nil)
+ (while (< begin end)
+ (push begin entry)
+ (setq begin (1+ begin)))
+ (push (cons "" entry) alist))
;; We got all the potential prefixes. Now create
;; `gnus-cite-prefix-alist' containing the oldest prefix for each
- ;; line that appears at least gnus-cite-minimum-match-count
+ ;; line that appears at least `gnus-cite-minimum-match-count'
;; times. First sort them by length. Longer is older.
(setq alist (sort alist (lambda (a b)
(> (length (car a)) (length (car b))))))
(let ((al (buffer-substring (save-excursion (beginning-of-line 0)
(1+ (point)))
end)))
- (if (not (assoc al al-alist))
- (progn
- (push (list wrote in prefix tag)
- gnus-cite-loose-attribution-alist)
- (push (cons al t) al-alist))))))))
+ (when (not (assoc al al-alist))
+ (push (list wrote in prefix tag)
+ gnus-cite-loose-attribution-alist)
+ (push (cons al t) al-alist)))))))
(defun gnus-cite-connect-attributions ()
;; Connect attributions to citations
(when face
(let ((inhibit-point-motion-hooks t)
from to overlay)
- (goto-line number)
- (unless (eobp) ; Sometimes things become confused.
+ (goto-char (point-min))
+ (when (zerop (forward-line (1- number)))
(forward-char (length prefix))
(skip-chars-forward " \t")
(setq from (point))
(when (< from to)
(push (setq overlay (gnus-make-overlay from to))
gnus-cite-overlay-list)
+ (gnus-overlay-put overlay 'evaporate t)
(gnus-overlay-put overlay 'face face))))))
(defun gnus-cite-toggle (prefix)
(save-excursion
(set-buffer gnus-article-buffer)
- (gnus-cite-parse-maybe)
+ (gnus-cite-parse-maybe nil t)
(let ((buffer-read-only nil)
(numbers (cdr (assoc prefix gnus-cite-prefix-alist)))
(inhibit-point-motion-hooks t)
(while numbers
(setq number (car numbers)
numbers (cdr numbers))
- (goto-line number)
+ (goto-char (point-min))
+ (forward-line (1- number))
(cond ((get-text-property (point) 'invisible)
+ ;; Can't remove 'cite from g-a-wash-types here because
+ ;; multiple citations may be hidden -jas
(remove-text-properties (point) (progn (forward-line 1) (point))
gnus-hidden-properties))
((assq number gnus-cite-attribution-alist))
(t
+ (gnus-add-wash-type 'cite)
(gnus-add-text-properties
(point) (progn (forward-line 1) (point))
(nconc (list 'article-type 'cite)
- gnus-hidden-properties))))))))
+ gnus-hidden-properties))))
+ (let ((gnus-article-mime-handle-alist-1
+ gnus-article-mime-handle-alist))
+ (gnus-set-mode-line 'article))))))
(defun gnus-cite-find-prefix (line)
;; Return citation prefix for LINE.
(while vars
(make-local-variable (pop vars)))))
+(defun gnus-cited-line-p ()
+ "Say whether the current line is a cited line."
+ (save-excursion
+ (beginning-of-line)
+ (let ((found nil))
+ (dolist (prefix (mapcar 'car gnus-cite-prefix-alist))
+ (when (string= (buffer-substring (point) (+ (length prefix) (point)))
+ prefix)
+ (setq found t)))
+ found)))
+
+
+;; Highlighting of different citation levels in message-mode.
+;;
+;; Known bugs:
+;;
+;; - XEmacs compatibility: `font-lock-add-keywords' is missing in XEmacs.
+;;
+;; - message-cite-prefix should not be fontified.
+
+(defconst gnus-message-max-citation-depth
+ (length gnus-cite-face-list)
+ "Maximum supported level of citation.")
+
+(defun gnus-message-search-citation-line (limit)
+ "Search for a cited line and set match data accordingly.
+Returns nil if there is no such line before LIMIT, t otherwise."
+ (when (re-search-forward (eval-when-compile
+ (concat "^\\(?:"
+ message-cite-prefix-regexp
+ "\\)"))
+ limit t)
+ (let ((cdepth
+ (length (apply 'concat
+ (split-string
+ (match-string-no-properties 0)
+ "[ \t [:alnum:]]+"))))
+ (mlist (make-list (* (1+ gnus-message-max-citation-depth)
+ 2)
+ 0)))
+ (setcar (nthcdr (* cdepth 2) mlist)
+ (line-beginning-position))
+ (setcar (nthcdr (1+ (* cdepth 2)) mlist)
+ (line-end-position))
+ (set-match-data mlist))
+ t))
+
+(defvar gnus-message-citation-keywords
+ ;; eval-when-compile ;; This breaks in XEmacs
+ `((gnus-message-search-citation-line
+ ,@(let ((list nil)
+ (count 1))
+ ;; (require 'gnus-cite)
+ (dolist (face gnus-cite-face-list (nreverse list))
+ (push (list count (list 'quote face) 'prepend) list)
+ (setq count (1+ count)))))) ;;
+ "Keywords for highlighting different levels of message citations.")
+
+(defun gnus-message-add-citation-keywords ()
+ "Add font-lock for nested citations to current buffer."
+ (if (fboundp 'font-lock-add-keywords)
+ (font-lock-add-keywords nil gnus-message-citation-keywords)
+ (gnus-message 1 "`font-lock-add-keywords' not supported.")))
+
+(defun gnus-message-remove-citation-keywords ()
+ "Remove font-lock for nested citations from current buffer."
+ (if (fboundp 'font-lock-remove-keywords)
+ (font-lock-remove-keywords nil gnus-message-citation-keywords)
+ (gnus-message 1 "`font-lock-remove-keywords' not supported.")))
+
+(define-minor-mode gnus-message-citation-mode
+ "Toggle `gnus-message-citation-mode' in current buffer.
+This buffer local minor mode provides additional font-lock support for
+nested citations.
+With prefix ARG, turn `gnus-message-citation-mode' on if and only if ARG is
+positive."
+ nil ;; init-value
+ "" ;; lighter
+ nil ;; keymap
+ (if gnus-message-citation-mode
+ (gnus-message-add-citation-keywords)
+ (gnus-message-remove-citation-keywords))
+ (font-lock-fontify-buffer))
+
+(defun turn-on-gnus-message-citation-mode ()
+ "Turn on `gnus-message-citation-mode'."
+ (gnus-message-citation-mode 1))
+(defun turn-off-gnus-message-citation-mode ()
+ "Turn off `gnus-message-citation-mode'."
+ (gnus-message-citation-mode -1))
+
(gnus-ems-redefine)
(provide 'gnus-cite)
+;; Local Variables:
+;; coding: iso-8859-1
+;; End:
+
+;;; arch-tag: 1997b044-6067-471e-8c8f-dc903093098a
;;; gnus-cite.el ends here