X-Git-Url: https://cgit.sxemacs.org/?a=blobdiff_plain;f=lisp%2Fgnus-cite.el;h=f0507a0c75901df211ae16b9a1742e1312161997;hb=b28454eed83f245c4160228b076134ce930b320a;hp=e8df3f23fbef47b1eaf438630b4de3fd18b016b2;hpb=a5526227c19752300e9e6a403289918a5c2f0bac;p=gnus diff --git a/lisp/gnus-cite.el b/lisp/gnus-cite.el index e8df3f23f..f0507a0c7 100644 --- a/lisp/gnus-cite.el +++ b/lisp/gnus-cite.el @@ -1,71 +1,263 @@ -;;; gnus-cite.el --- Highlight GNUS article. -;; Copyright (C) 1995 Free Software Foundation, Inc. +;;; gnus-cite.el --- parse citations in articles for Gnus +;; Copyright (C) 1995,96,97 Free Software Foundation, Inc. ;; Author: Per Abrahamsen ;; Keywords: news, mail +;; 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 +;; the Free Software Foundation; either version 2, 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 +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; 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. + ;;; Commentary: ;;; Code: (require 'gnus) -(require 'gnus-vis) +(require 'gnus-art) +(require 'gnus-range) ;;; Customization: -(defvar gnus-cite-prefix-regexp "^[^\n]*[]>|:}+]" - "Regexp matching the longest possible citation prefix on a line.") - -(defvar gnus-cite-max-prefix 20 - "Maximal possible length for a citation prefix.") - -(defvar gnus-supercite-regexp +(defgroup gnus-cite nil + "Citation." + :prefix "gnus-cite-" + :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-text-button-line-format "%(%{[...]%}%)\n" + "Format of 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." + :group 'gnus-cite + :type '(choice (const :tag "none" nil) + integer)) + +(defcustom gnus-cite-parse-max-size 25000 + "Maximum article size (in bytes) where parsing citations is allowed. +Set it to nil to parse all articles." + :group 'gnus-cite + :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 "\\)? *" ">>>>> +\"\\([^\"\n]+\\)\" +==") - "Regexp matching normal SuperCite attribution lines. -The first regexp group should match a prefix added by another package. -The second regexp group should match the SuperCite attribution itself.") - -(defvar gnus-supercite-secondary-regexp "^.*\"\\([^\"\n]+\\)\" +==" - "Regexp matching mangled SuperCite attribution lines. -The first regexp group should match the SuperCite attribution.") - -(defvar gnus-cite-minimum-match-count 2 - "Minimal number of identical prefix'es before we believe it is a citation.") - -(defvar gnus-cite-face-list '(italic) - "Faces used for displaying different citations. -It is either a list of face names, or one of the following special -values: - -dark: Create faces from `gnus-face-dark-name-list'. -light: Create faces from `gnus-face-light-name-list'. - -The variable `gnus-make-foreground' determines whether the created -faces change the foreground or the background colors.") - -(defvar gnus-cite-attribution-prefix "in article\\|in <" - "Regexp matching the beginning of an attribution line.") - -(defvar gnus-cite-attribution-postfix "\\(wrote\\|writes\\|said\\):[ \t]*$" + "Regexp matching normal Supercite attribution lines. +The first grouping must match prefixes added by other packages." + :group 'gnus-cite + :type 'regexp) + +(defcustom gnus-supercite-secondary-regexp "^.*\"\\([^\"\n]+\\)\" +==" + "Regexp matching mangled Supercite attribution lines. +The first regexp group should match the Supercite attribution." + :group 'gnus-cite + :type 'regexp) + +(defcustom gnus-cite-minimum-match-count 2 + "Minimum number of identical prefixes before we believe it's a citation." + :group 'gnus-cite + :type 'integer) + +(defcustom gnus-cite-attribution-prefix "in article\\|in <" + "Regexp matching the beginning of an attribution line." + :group 'gnus-cite + :type 'regexp) + +(defcustom gnus-cite-attribution-suffix + "\\(wrote\\|writes\\|said\\|says\\):[ \t]*$" "Regexp matching the end of an attribution line. -The text matching the first grouping will be used as a button.") +The text matching the first grouping will be used as a button." + :group 'gnus-cite + :type 'regexp) -(defvar gnus-cite-attribution-face 'underline - "Face used for attribution lines. -It is merged with the face for the cited text belonging to the attribution.") - -(defvar gnus-cite-hide-percentage 30 - "Only hide cited text if it is larger than this percent of the body.") - -(defvar gnus-cite-hide-absolute 5 - "Only hide cited text if there is at least this number of cited lines.") +(defface gnus-cite-attribution-face '((t + (:underline t))) + "Face used for attribution lines.") +(defcustom gnus-cite-attribution-face 'gnus-cite-attribution-face + "Face used for attribution lines. +It is merged with the face for the cited text belonging to the attribution." + :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.") + +(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) + "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)) + +(defcustom gnus-cite-hide-percentage 50 + "Only hide excess citation if above this percentage of the body." + :group 'gnus-cite + :type 'number) + +(defcustom gnus-cite-hide-absolute 10 + "Only hide excess citation if above this number of lines in the body." + :group 'gnus-cite + :type 'integer) ;;; Internal Variables: +(defvar gnus-cite-article nil) + (defvar gnus-cite-prefix-alist nil) -;; Alist of citation prefixes. +;; Alist of citation prefixes. ;; The cdr is a list of lines with that prefix. (defvar gnus-cite-attribution-alist nil) @@ -83,37 +275,37 @@ It is merged with the face for the cited text belonging to the attribution.") ;; WROTE: is the attribution line number ;; IN: is the line number of the previous line if part of the same attribution, ;; PREFIX: Is the citation prefix of the attribution line(s), and -;; TAG: Is a SuperCite tag, if any. +;; TAG: Is a Supercite tag, if any. + +(defvar gnus-cited-text-button-line-format-alist + `((?b (marker-position beg) ?d) + (?e (marker-position end) ?d) + (?l (- end beg) ?d))) +(defvar gnus-cited-text-button-line-format-spec nil) ;;; Commands: -(defun gnus-article-highlight-citation () +(defun gnus-article-highlight-citation (&optional force) "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 sameface as the +Attribution lines are highlighted with the same face as the corresponding citation merged with `gnus-cite-attribution-face'. -Text is concidered cited if at least `gnus-cite-minimum-match-count' -lines matches `gnus-cite-prefix-regexp' with the same prefix. +Text is considered cited if at least `gnus-cite-minimum-match-count' +lines matches `gnus-cite-prefix-regexp' with the same prefix. -Lines matching `gnus-cite-attribution-postfix' and perhaps -`gnus-cite-attribution-prefix' are concidered attribution lines." - (interactive) - ;; Create dark or light faces if necessary. - (cond ((eq gnus-cite-face-list 'light) - (setq gnus-cite-face-list - (mapcar 'gnus-make-face gnus-face-light-name-list))) - ((eq gnus-cite-face-list 'dark) - (setq gnus-cite-face-list - (mapcar 'gnus-make-face gnus-face-dark-name-list)))) +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) - (gnus-cite-parse-maybe) + (gnus-cite-parse-maybe force) (let ((buffer-read-only nil) (alist gnus-cite-prefix-alist) (faces gnus-cite-face-list) - face entry prefix skip numbers number face-alist end) + (inhibit-point-motion-hooks t) + face entry prefix skip numbers number face-alist) ;; Loop through citation prefixes. (while alist (setq entry (car alist) @@ -140,11 +332,11 @@ Lines matching `gnus-cite-attribution-postfix' and perhaps face (cdr (assoc prefix face-alist))) ;; Add attribution button. (goto-line number) - (if (re-search-forward gnus-cite-attribution-postfix - (save-excursion (end-of-line 1) (point)) - t) - (gnus-article-add-button (match-beginning 1) (match-end 1) - 'gnus-cite-toggle prefix)) + (when (re-search-forward gnus-cite-attribution-suffix + (save-excursion (end-of-line 1) (point)) + t) + (gnus-article-add-button (match-beginning 1) (match-end 1) + 'gnus-cite-toggle prefix)) ;; Highlight attribution line. (gnus-cite-add-face number skip face) (gnus-cite-add-face number skip gnus-cite-attribution-face)) @@ -157,93 +349,270 @@ Lines matching `gnus-cite-attribution-postfix' and perhaps skip (gnus-cite-find-prefix number)) (gnus-cite-add-face number skip gnus-cite-attribution-face))))) -(defun gnus-article-hide-citation () - "Hide all cited text except attribution lines. -See the documentation for `gnus-article-highlight-citation'." - (interactive) +(defun gnus-dissect-cited-text () + "Dissect the article buffer looking for cited text." (save-excursion (set-buffer gnus-article-buffer) (gnus-cite-parse-maybe) - (let ((buffer-read-only nil) - (alist gnus-cite-prefix-alist) - numbers number) + (let ((alist gnus-cite-prefix-alist) + prefix numbers number marks m) + ;; Loop through citation prefixes. (while alist - (setq numbers (cdr (car alist)) - alist (cdr alist)) + (setq numbers (pop alist) + prefix (pop numbers)) (while numbers - (setq number (car numbers) - numbers (cdr numbers)) - (goto-line number) - (or (assq number gnus-cite-attribution-alist) - (put-text-property (point) (progn (forward-line 1) (point)) - 'invisible t))))))) - -(defun gnus-article-hide-citation-maybe (&optional force) - "Hide cited text that has an attribution line. + (setq number (pop numbers)) + (goto-char (point-min)) + (forward-line number) + (push (cons (point-marker) "") marks) + (while (and numbers + (= (1- number) (car numbers))) + (setq number (pop numbers))) + (goto-char (point-min)) + (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) + (push (cons (point-marker) "") marks) + ;; Find the end of the body. + (goto-char (point-max)) + (gnus-article-search-signature) + (push (cons (point-marker) "") marks) + ;; Sort the marks. + (setq marks (sort marks (lambda (m1 m2) (< (car m1) (car m2))))) + (let ((omarks marks)) + (setq marks nil) + (while (cdr omarks) + (if (= (caar omarks) (caadr omarks)) + (progn + (unless (equal (cdar omarks) "") + (push (car omarks) marks)) + (unless (equal (cdadr omarks) "") + (push (cadr omarks) marks)) + (unless (and (equal (cdar omarks) "") + (equal (cdadr omarks) "") + (not (cddr omarks))) + (setq omarks (cdr omarks)))) + (push (car omarks) marks)) + (setq omarks (cdr omarks))) + (when (car omarks) + (push (car omarks) marks)) + (setq marks (setq m (nreverse marks))) + (while (cddr m) + (if (and (equal (cdadr m) "") + (equal (cdar m) (cdaddr m)) + (goto-char (caadr m)) + (forward-line 1) + (= (point) (caaddr m))) + (setcdr m (cdddr m)) + (setq m (cdr m)))) + marks)))) + +(defun gnus-article-fill-cited-article (&optional force width) + "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) + (let ((buffer-read-only nil) + (inhibit-point-motion-hooks t) + (marks (gnus-dissect-cited-text)) + (adaptive-fill-mode nil) + (filladapt-mode nil) + (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-region (point-min) (point-max))) + (set-marker (caar marks) nil) + (setq marks (cdr marks))) + (when marks + (set-marker (caar marks) nil)) + ;; All this information is now incorrect. + (setq gnus-cite-prefix-alist nil + gnus-cite-attribution-alist nil + gnus-cite-loose-prefix-alist nil + gnus-cite-loose-attribution-alist nil))))) + +(defun gnus-article-hide-citation (&optional arg force) + "Toggle hiding of all cited text except attribution lines. +See the documentation for `gnus-article-highlight-citation'. +If given a negative prefix, always show; if given a positive prefix, +always hide." + (interactive (append (gnus-article-hidden-arg) (list 'force))) + (setq gnus-cited-text-button-line-format-spec + (gnus-parse-format gnus-cited-text-button-line-format + gnus-cited-text-button-line-format-alist 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)) + (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 + (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 + (gnus-article-add-button + (point) + (progn (eval gnus-cited-text-button-line-format-spec) (point)) + `gnus-article-toggle-cited-text (cons beg end)) + (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))) + +(defun gnus-article-hide-citation-maybe (&optional arg force) + "Toggle hiding of cited text that has an attribution line. +If given a negative prefix, always show; if given a positive prefix, +always hide. This will do nothing unless at least `gnus-cite-hide-percentage' -percent ans at least `gnus-cite-hide-absolute' lines of the body is +percent and at least `gnus-cite-hide-absolute' lines of the body is cited text with attributions. When called interactively, these two variables are ignored. See also the documentation for `gnus-article-highlight-citation'." - (interactive (list 'force)) + (interactive (append (gnus-article-hidden-arg) (list '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) + (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)))))))))) + +(defun gnus-article-hide-citation-in-followups () + "Hide cited text in non-root articles." + (interactive) (save-excursion (set-buffer gnus-article-buffer) - (gnus-cite-parse-maybe) - (goto-char (point-min)) - (search-forward "\n\n") - (let ((start (point)) - (atts gnus-cite-attribution-alist) - (buffer-read-only nil) - (hiden 0) - total) - (goto-char (point-max)) - (re-search-backward gnus-signature-separator nil t) - (setq total (count-lines start (point))) - (while atts - (setq hiden (+ hiden (length (cdr (assoc (cdr (car atts)) - gnus-cite-prefix-alist)))) - atts (cdr atts))) - (if (or force - (and (> (* 100 hiden) (* gnus-cite-hide-percentage total)) - (> hiden gnus-cite-hide-absolute))) - (progn - (setq atts gnus-cite-attribution-alist) - (while atts - (setq total (cdr (assoc (cdr (car atts)) gnus-cite-prefix-alist)) - atts (cdr atts)) - (while total - (setq hiden (car total) - total (cdr total)) - (goto-line hiden) - (or (assq hiden gnus-cite-attribution-alist) - (put-text-property (point) (progn (forward-line 1) (point)) - 'invisible t))))))))) + (let ((article (cdr gnus-article-current))) + (unless (save-excursion + (set-buffer gnus-summary-buffer) + (gnus-article-displayed-root-p article)) + (gnus-article-hide-citation))))) ;;; Internal functions: -(defun gnus-cite-parse-maybe () +(defun gnus-cite-parse-maybe (&optional force) ;; Parse if the buffer has changes since last time. - (if (eq gnus-article-length (- (point-max) (point-min))) + (if (equal gnus-cite-article gnus-article-current) () - (setq gnus-article-length (- (point-max) (point-min))) - (gnus-cite-parse))) + ;;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) + ;; 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)))) + +(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))))) (defun gnus-cite-parse () ;; Parse and connect citation prefixes and attribution lines. - (setq gnus-cite-prefix-alist nil - gnus-cite-attribution-alist nil - gnus-cite-loose-prefix-alist nil - gnus-cite-loose-attribution-alist nil) + ;; Parse current buffer searching for citation prefixes. - (goto-char (point-min)) - (search-forward "\n\n") (let ((line (1+ (count-lines (point-min) (point)))) (case-fold-search t) (max (save-excursion (goto-char (point-max)) - (re-search-backward gnus-signature-separator nil t) + (gnus-article-search-signature) (point))) - alist entry prefix start begin end numbers) + alist entry start begin end numbers prefix) ;; Get all potential prefixes in `alist'. (while (< (point) max) ;; Each line. @@ -251,30 +620,30 @@ See also the documentation for `gnus-article-highlight-citation'." end (progn (beginning-of-line 2) (point)) start end) (goto-char begin) - ;; Ignore standard SuperCite attribution prefix. - (if (looking-at gnus-supercite-regexp) - (if (match-end 1) - (setq end (1+ (match-end 1))) - (setq end (1+ begin)))) + ;; Ignore standard Supercite attribution prefix. + (when (looking-at gnus-supercite-regexp) + (if (match-end 1) + (setq end (1+ (match-end 1))) + (setq end (1+ begin)))) ;; Ignore very long prefixes. - (if (> end (+ (point) gnus-cite-max-prefix)) - (setq end (+ (point) gnus-cite-max-prefix))) + (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) ;; Each prefix. (setq end (match-end 0) prefix (buffer-substring begin end)) - (set-text-properties 0 (length prefix) nil prefix) + (gnus-set-text-properties 0 (length prefix) nil prefix) (setq entry (assoc prefix alist)) - (if entry + (if entry (setcdr entry (cons line (cdr entry))) - (setq alist (cons (list prefix line) alist))) + (push (list prefix line) alist)) (goto-char begin)) (goto-char start) (setq line (1+ line))) ;; 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 - ;; times. First sort them by length. Longer is older. + ;; times. First sort them by length. Longer is older. (setq alist (sort alist (lambda (a b) (> (length (car a)) (length (car b)))))) (while alist @@ -290,85 +659,104 @@ See also the documentation for `gnus-article-highlight-citation'." ;; Too few lines with this prefix. We keep it a bit ;; longer in case it is an exact match for an attribution ;; line, but we don't remove the line from other - ;; prefixes. - (setq gnus-cite-prefix-alist - (cons entry gnus-cite-prefix-alist))) + ;; prefixes. + (push entry gnus-cite-prefix-alist)) (t - (setq gnus-cite-prefix-alist (cons entry gnus-cite-prefix-alist)) + (push entry + gnus-cite-prefix-alist) ;; Remove articles from other prefixes. (let ((loop alist) current) (while loop (setq current (car loop) loop (cdr loop)) - (setcdr current - (gnus-set-difference (cdr current) numbers)))))))) + (setcdr current + (gnus-set-difference (cdr current) numbers))))))))) + +(defun gnus-cite-parse-attributions () + (let (al-alist) + ;; Parse attributions + (while (re-search-forward gnus-cite-attribution-suffix (point-max) t) + (let* ((start (match-beginning 0)) + (end (match-end 0)) + (wrote (count-lines (point-min) end)) + (prefix (gnus-cite-find-prefix wrote)) + ;; Check previous line for an attribution leader. + (tag (progn + (beginning-of-line 1) + (when (looking-at gnus-supercite-secondary-regexp) + (buffer-substring (match-beginning 1) + (match-end 1))))) + (in (progn + (goto-char start) + (and (re-search-backward gnus-cite-attribution-prefix + (save-excursion + (beginning-of-line 0) + (point)) + t) + (not (re-search-forward gnus-cite-attribution-suffix + start t)) + (count-lines (point-min) (1+ (point))))))) + (when (eq wrote in) + (setq in nil)) + (goto-char end) + ;; don't add duplicates + (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)))))))) + +(defun gnus-cite-connect-attributions () + ;; Connect attributions to citations + ;; No citations have been connected to attribution lines yet. (setq gnus-cite-loose-prefix-alist (append gnus-cite-prefix-alist nil)) ;; Parse current buffer searching for attribution lines. - (goto-char (point-min)) - (search-forward "\n\n") - (while (re-search-forward gnus-cite-attribution-postfix (point-max) t) - (let* ((start (match-beginning 0)) - (end (match-end 0)) - (wrote (count-lines (point-min) end)) - (prefix (gnus-cite-find-prefix wrote)) - ;; Check previous line for an attribution leader. - (tag (progn - (beginning-of-line 1) - (and (looking-at gnus-supercite-secondary-regexp) - (buffer-substring (match-beginning 1) - (match-end 1))))) - (in (progn - (goto-char start) - (and (re-search-backward gnus-cite-attribution-prefix - (save-excursion - (beginning-of-line 0) - (point)) - t) - (not (re-search-forward gnus-cite-attribution-postfix - start t)) - (count-lines (point-min) (1+ (point))))))) - (if (eq wrote in) - (setq in nil)) - (goto-char end) - (setq gnus-cite-loose-attribution-alist - (cons (list wrote in prefix tag) - gnus-cite-loose-attribution-alist)))) ;; Find exact supercite citations. (gnus-cite-match-attributions 'small nil - (lambda (prefix tag) - (if tag - (concat "\\`" (regexp-quote prefix) "[ \t]*" - (regexp-quote tag) ">")))) + (lambda (prefix tag) + (when tag + (concat "\\`" + (regexp-quote prefix) "[ \t]*" + (regexp-quote tag) ">")))) ;; Find loose supercite citations after attributions. (gnus-cite-match-attributions 'small t - (lambda (prefix tag) - (if tag (concat "\\<" (regexp-quote tag) "\\>")))) + (lambda (prefix tag) + (when tag + (concat "\\<" + (regexp-quote tag) + "\\>")))) ;; Find loose supercite citations anywhere. (gnus-cite-match-attributions 'small nil - (lambda (prefix tag) - (if tag (concat "\\<" (regexp-quote tag) "\\>")))) + (lambda (prefix tag) + (when tag + (concat "\\<" + (regexp-quote tag) + "\\>")))) ;; Find nested citations after attributions. (gnus-cite-match-attributions 'small-if-unique t - (lambda (prefix tag) - (concat "\\`" (regexp-quote prefix) ".+"))) + (lambda (prefix tag) + (concat "\\`" (regexp-quote prefix) ".+"))) ;; Find nested citations anywhere. (gnus-cite-match-attributions 'small nil - (lambda (prefix tag) - (concat "\\`" (regexp-quote prefix) ".+"))) + (lambda (prefix tag) + (concat "\\`" (regexp-quote prefix) ".+"))) ;; Remove loose prefixes with too few lines. (let ((alist gnus-cite-loose-prefix-alist) - entry prefix) + entry) (while alist (setq entry (car alist) alist (cdr alist)) - (if (< (length (cdr entry)) gnus-cite-minimum-match-count) - (setq gnus-cite-prefix-alist - (delq entry gnus-cite-prefix-alist) - gnus-cite-loose-prefix-alist - (delq entry gnus-cite-loose-prefix-alist))))) + (when (< (length (cdr entry)) gnus-cite-minimum-match-count) + (setq gnus-cite-prefix-alist + (delq entry gnus-cite-prefix-alist) + gnus-cite-loose-prefix-alist + (delq entry gnus-cite-loose-prefix-alist))))) ;; Find flat attributions. (gnus-cite-match-attributions 'first t nil) ;; Find any attributions (are we getting desperate yet?). @@ -384,19 +772,19 @@ See also the documentation for `gnus-article-highlight-citation'." ;; loose attribution lines, otherwise the first prefix will be used. ;; ;; If AFTER is non-nil, only citations after the attribution line - ;; will be concidered. + ;; will be considered. ;; ;; If FUN is non-nil, it will be called with the arguments (WROTE ;; PREFIX TAG) and expected to return a regular expression. Only ;; citations whose prefix matches the regular expression will be - ;; concidered. - ;; + ;; considered. + ;; ;; WROTE is the attribution line number. ;; PREFIX is the attribution line prefix. - ;; TAG is the SuperCite tag on the attribution line. + ;; TAG is the Supercite tag on the attribution line. (let ((atts gnus-cite-loose-attribution-alist) (case-fold-search t) - att wrote in prefix tag regexp limit smallest best size aprefix) + att wrote in prefix tag regexp limit smallest best size) (while atts (setq att (car atts) atts (cdr atts) @@ -409,7 +797,7 @@ See also the documentation for `gnus-article-highlight-citation'." ((eq sort 'first) nil) (t (< (length (gnus-cite-find-loose prefix)) 2))) limit (if after wrote -1) - smallest 1000000 + smallest 1000000 best nil) (let ((cites gnus-cite-loose-prefix-alist) cite candidate numbers first compare) @@ -430,71 +818,74 @@ See also the documentation for `gnus-article-highlight-citation'." () (setq gnus-cite-loose-attribution-alist (delq att gnus-cite-loose-attribution-alist)) - (setq gnus-cite-attribution-alist - (cons (cons wrote (car best)) gnus-cite-attribution-alist)) - (if in - (setq gnus-cite-attribution-alist - (cons (cons in (car best)) gnus-cite-attribution-alist))) - (if (memq best gnus-cite-loose-prefix-alist) - (let ((loop gnus-cite-prefix-alist) - (numbers (cdr best)) - current) - (setq gnus-cite-loose-prefix-alist - (delq best gnus-cite-loose-prefix-alist)) - (while loop - (setq current (car loop) - loop (cdr loop)) - (if (eq current best) - () - (setcdr current (gnus-set-difference (cdr current) numbers)) - (if (null (cdr current)) - (setq gnus-cite-loose-prefix-alist - (delq current gnus-cite-loose-prefix-alist) - atts (delq current atts))))))))))) + (push (cons wrote (car best)) gnus-cite-attribution-alist) + (when in + (push (cons in (car best)) gnus-cite-attribution-alist)) + (when (memq best gnus-cite-loose-prefix-alist) + (let ((loop gnus-cite-prefix-alist) + (numbers (cdr best)) + current) + (setq gnus-cite-loose-prefix-alist + (delq best gnus-cite-loose-prefix-alist)) + (while loop + (setq current (car loop) + loop (cdr loop)) + (if (eq current best) + () + (setcdr current (gnus-set-difference (cdr current) numbers)) + (when (null (cdr current)) + (setq gnus-cite-loose-prefix-alist + (delq current gnus-cite-loose-prefix-alist) + atts (delq current atts))))))))))) (defun gnus-cite-find-loose (prefix) ;; Return a list of loose attribution lines prefixed by PREFIX. (let* ((atts gnus-cite-loose-attribution-alist) - att line lines candidate) + att line lines) (while atts (setq att (car atts) line (car att) atts (cdr atts)) - (if (string-equal (gnus-cite-find-prefix line) prefix) - (setq lines (cons line lines)))) + (when (string-equal (gnus-cite-find-prefix line) prefix) + (push line lines))) lines)) (defun gnus-cite-add-face (number prefix face) ;; At line NUMBER, ignore PREFIX and add FACE to the rest of the line. - (if face - (let (from to) - (goto-line number) + (when face + (let ((inhibit-point-motion-hooks t) + from to) + (goto-line number) + (unless (eobp);; Sometimes things become confused. (forward-char (length prefix)) (skip-chars-forward " \t") (setq from (point)) (end-of-line 1) (skip-chars-backward " \t") (setq to (point)) - (if (< from to) - (overlay-put (make-overlay from to) 'face face))))) + (when (< from to) + (gnus-overlay-put (gnus-make-overlay from to) 'face face)))))) (defun gnus-cite-toggle (prefix) (save-excursion (set-buffer gnus-article-buffer) (let ((buffer-read-only nil) (numbers (cdr (assoc prefix gnus-cite-prefix-alist))) + (inhibit-point-motion-hooks t) number) (while numbers (setq number (car numbers) numbers (cdr numbers)) (goto-line number) (cond ((get-text-property (point) 'invisible) - (put-text-property (point) (progn (forward-line 1) (point)) - 'invisible nil)) + (remove-text-properties (point) (progn (forward-line 1) (point)) + gnus-hidden-properties)) ((assq number gnus-cite-attribution-alist)) (t - (put-text-property (point) (progn (forward-line 1) (point)) - 'invisible t))))))) + (gnus-add-text-properties + (point) (progn (forward-line 1) (point)) + (nconc (list 'article-type 'cite) + gnus-hidden-properties)))))))) (defun gnus-cite-find-prefix (line) ;; Return citation prefix for LINE. @@ -504,10 +895,17 @@ See also the documentation for `gnus-article-highlight-citation'." (while alist (setq entry (car alist) alist (cdr alist)) - (if (memq line (cdr entry)) - (setq prefix (car entry)))) + (when (memq line (cdr entry)) + (setq prefix (car entry)))) prefix)) +(gnus-add-shutdown 'gnus-cache-close 'gnus) + +(defun gnus-cache-close () + (setq gnus-cite-prefix-alist nil)) + +(gnus-ems-redefine) + (provide 'gnus-cite) ;;; gnus-cite.el ends here