Mercurial > emacs
annotate lisp/gnus/gnus-cite.el @ 20483:141c2d41d39c
(toggle-enable-multibyte-characters): Doc fix.
author | Richard M. Stallman <rms@gnu.org> |
---|---|
date | Sun, 21 Dec 1997 01:49:03 +0000 |
parents | 5f1ab3dd344d |
children | 15fc6acbae7a |
rev | line source |
---|---|
17493 | 1 ;;; gnus-cite.el --- parse citations in articles for Gnus |
2 ;; Copyright (C) 1995,96,97 Free Software Foundation, Inc. | |
3 | |
4 ;; Author: Per Abrahamsen <abraham@iesd.auc.dk> | |
5 ;; Keywords: news, mail | |
6 | |
7 ;; This file is part of GNU Emacs. | |
8 | |
9 ;; GNU Emacs is free software; you can redistribute it and/or modify | |
10 ;; it under the terms of the GNU General Public License as published by | |
11 ;; the Free Software Foundation; either version 2, or (at your option) | |
12 ;; any later version. | |
13 | |
14 ;; GNU Emacs is distributed in the hope that it will be useful, | |
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 ;; GNU General Public License for more details. | |
18 | |
19 ;; You should have received a copy of the GNU General Public License | |
20 ;; along with GNU Emacs; see the file COPYING. If not, write to the | |
21 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
22 ;; Boston, MA 02111-1307, USA. | |
23 | |
24 ;;; Commentary: | |
25 | |
26 ;;; Code: | |
27 | |
19521
6f6cf9184e93
Require cl at compile time.
Richard M. Stallman <rms@gnu.org>
parents:
17493
diff
changeset
|
28 (eval-when-compile (require 'cl)) |
6f6cf9184e93
Require cl at compile time.
Richard M. Stallman <rms@gnu.org>
parents:
17493
diff
changeset
|
29 |
17493 | 30 (require 'gnus) |
31 (require 'gnus-art) | |
32 (require 'gnus-range) | |
33 | |
34 ;;; Customization: | |
35 | |
36 (defgroup gnus-cite nil | |
37 "Citation." | |
38 :prefix "gnus-cite-" | |
39 :link '(custom-manual "(gnus)Article Highlighting") | |
40 :group 'gnus-article) | |
41 | |
42 (defcustom gnus-cite-reply-regexp | |
43 "^\\(Subject: Re\\|In-Reply-To\\|References\\):" | |
44 "If headers match this regexp it is reasonable to believe that | |
45 article has citations." | |
46 :group 'gnus-cite | |
47 :type 'string) | |
48 | |
49 (defcustom gnus-cite-always-check nil | |
50 "Check article always for citations. Set it t to check all articles." | |
51 :group 'gnus-cite | |
52 :type '(choice (const :tag "no" nil) | |
53 (const :tag "yes" t))) | |
54 | |
55 (defcustom gnus-cited-text-button-line-format "%(%{[...]%}%)\n" | |
56 "Format of cited text buttons." | |
57 :group 'gnus-cite | |
58 :type 'string) | |
59 | |
60 (defcustom gnus-cited-lines-visible nil | |
61 "The number of lines of hidden cited text to remain visible." | |
62 :group 'gnus-cite | |
63 :type '(choice (const :tag "none" nil) | |
64 integer)) | |
65 | |
66 (defcustom gnus-cite-parse-max-size 25000 | |
67 "Maximum article size (in bytes) where parsing citations is allowed. | |
68 Set it to nil to parse all articles." | |
69 :group 'gnus-cite | |
70 :type '(choice (const :tag "all" nil) | |
71 integer)) | |
72 | |
73 (defcustom gnus-cite-prefix-regexp | |
74 "^[]>|:}+ ]*[]>|:}+]\\(.*>\\)?\\|^.*>" | |
75 "Regexp matching the longest possible citation prefix on a line." | |
76 :group 'gnus-cite | |
77 :type 'regexp) | |
78 | |
79 (defcustom gnus-cite-max-prefix 20 | |
80 "Maximum possible length for a citation prefix." | |
81 :group 'gnus-cite | |
82 :type 'integer) | |
83 | |
84 (defcustom gnus-supercite-regexp | |
85 (concat "^\\(" gnus-cite-prefix-regexp "\\)? *" | |
86 ">>>>> +\"\\([^\"\n]+\\)\" +==") | |
87 "Regexp matching normal Supercite attribution lines. | |
88 The first grouping must match prefixes added by other packages." | |
89 :group 'gnus-cite | |
90 :type 'regexp) | |
91 | |
92 (defcustom gnus-supercite-secondary-regexp "^.*\"\\([^\"\n]+\\)\" +==" | |
93 "Regexp matching mangled Supercite attribution lines. | |
94 The first regexp group should match the Supercite attribution." | |
95 :group 'gnus-cite | |
96 :type 'regexp) | |
97 | |
98 (defcustom gnus-cite-minimum-match-count 2 | |
99 "Minimum number of identical prefixes before we believe it's a citation." | |
100 :group 'gnus-cite | |
101 :type 'integer) | |
102 | |
19969
5f1ab3dd344d
*** empty log message ***
Lars Magne Ingebrigtsen <larsi@gnus.org>
parents:
19521
diff
changeset
|
103 (defcustom gnus-cite-attribution-prefix |
5f1ab3dd344d
*** empty log message ***
Lars Magne Ingebrigtsen <larsi@gnus.org>
parents:
19521
diff
changeset
|
104 "in article\\|in <\\|On \\(Mon\\|Tue\\|Wed\\|Thu\\|Fri\\|Sat\\|Sun\\)," |
17493 | 105 "Regexp matching the beginning of an attribution line." |
106 :group 'gnus-cite | |
107 :type 'regexp) | |
108 | |
109 (defcustom gnus-cite-attribution-suffix | |
19969
5f1ab3dd344d
*** empty log message ***
Lars Magne Ingebrigtsen <larsi@gnus.org>
parents:
19521
diff
changeset
|
110 "\\(\\(wrote\\|writes\\|said\\|says\\|>\\)\\(:\\|\\.\\.\\.\\)\\)[ ]*$" |
17493 | 111 "Regexp matching the end of an attribution line. |
112 The text matching the first grouping will be used as a button." | |
113 :group 'gnus-cite | |
114 :type 'regexp) | |
115 | |
116 (defface gnus-cite-attribution-face '((t | |
117 (:underline t))) | |
118 "Face used for attribution lines.") | |
119 | |
120 (defcustom gnus-cite-attribution-face 'gnus-cite-attribution-face | |
121 "Face used for attribution lines. | |
122 It is merged with the face for the cited text belonging to the attribution." | |
123 :group 'gnus-cite | |
124 :type 'face) | |
125 | |
126 (defface gnus-cite-face-1 '((((class color) | |
127 (background dark)) | |
128 (:foreground "light blue")) | |
129 (((class color) | |
130 (background light)) | |
131 (:foreground "MidnightBlue")) | |
132 (t | |
133 (:italic t))) | |
134 "Citation face.") | |
135 | |
136 (defface gnus-cite-face-2 '((((class color) | |
137 (background dark)) | |
138 (:foreground "light cyan")) | |
139 (((class color) | |
140 (background light)) | |
141 (:foreground "firebrick")) | |
142 (t | |
143 (:italic t))) | |
144 "Citation face.") | |
145 | |
146 (defface gnus-cite-face-3 '((((class color) | |
147 (background dark)) | |
148 (:foreground "light yellow")) | |
149 (((class color) | |
150 (background light)) | |
151 (:foreground "dark green")) | |
152 (t | |
153 (:italic t))) | |
154 "Citation face.") | |
155 | |
156 (defface gnus-cite-face-4 '((((class color) | |
157 (background dark)) | |
158 (:foreground "light pink")) | |
159 (((class color) | |
160 (background light)) | |
161 (:foreground "OrangeRed")) | |
162 (t | |
163 (:italic t))) | |
164 "Citation face.") | |
165 | |
166 (defface gnus-cite-face-5 '((((class color) | |
167 (background dark)) | |
168 (:foreground "pale green")) | |
169 (((class color) | |
170 (background light)) | |
171 (:foreground "dark khaki")) | |
172 (t | |
173 (:italic t))) | |
174 "Citation face.") | |
175 | |
176 (defface gnus-cite-face-6 '((((class color) | |
177 (background dark)) | |
178 (:foreground "beige")) | |
179 (((class color) | |
180 (background light)) | |
181 (:foreground "dark violet")) | |
182 (t | |
183 (:italic t))) | |
184 "Citation face.") | |
185 | |
186 (defface gnus-cite-face-7 '((((class color) | |
187 (background dark)) | |
188 (:foreground "orange")) | |
189 (((class color) | |
190 (background light)) | |
191 (:foreground "SteelBlue4")) | |
192 (t | |
193 (:italic t))) | |
194 "Citation face.") | |
195 | |
196 (defface gnus-cite-face-8 '((((class color) | |
197 (background dark)) | |
198 (:foreground "magenta")) | |
199 (((class color) | |
200 (background light)) | |
201 (:foreground "magenta")) | |
202 (t | |
203 (:italic t))) | |
204 "Citation face.") | |
205 | |
206 (defface gnus-cite-face-9 '((((class color) | |
207 (background dark)) | |
208 (:foreground "violet")) | |
209 (((class color) | |
210 (background light)) | |
211 (:foreground "violet")) | |
212 (t | |
213 (:italic t))) | |
214 "Citation face.") | |
215 | |
216 (defface gnus-cite-face-10 '((((class color) | |
217 (background dark)) | |
218 (:foreground "medium purple")) | |
219 (((class color) | |
220 (background light)) | |
221 (:foreground "medium purple")) | |
222 (t | |
223 (:italic t))) | |
224 "Citation face.") | |
225 | |
226 (defface gnus-cite-face-11 '((((class color) | |
227 (background dark)) | |
228 (:foreground "turquoise")) | |
229 (((class color) | |
230 (background light)) | |
231 (:foreground "turquoise")) | |
232 (t | |
233 (:italic t))) | |
234 "Citation face.") | |
235 | |
236 (defcustom gnus-cite-face-list | |
237 '(gnus-cite-face-1 gnus-cite-face-2 gnus-cite-face-3 gnus-cite-face-4 | |
238 gnus-cite-face-5 gnus-cite-face-6 gnus-cite-face-7 gnus-cite-face-8 | |
239 gnus-cite-face-9 gnus-cite-face-10 gnus-cite-face-11) | |
240 "List of faces used for highlighting citations. | |
241 | |
242 When there are citations from multiple articles in the same message, | |
243 Gnus will try to give each citation from each article its own face. | |
244 This should make it easier to see who wrote what." | |
245 :group 'gnus-cite | |
246 :type '(repeat face)) | |
247 | |
248 (defcustom gnus-cite-hide-percentage 50 | |
249 "Only hide excess citation if above this percentage of the body." | |
250 :group 'gnus-cite | |
251 :type 'number) | |
252 | |
253 (defcustom gnus-cite-hide-absolute 10 | |
254 "Only hide excess citation if above this number of lines in the body." | |
255 :group 'gnus-cite | |
256 :type 'integer) | |
257 | |
258 ;;; Internal Variables: | |
259 | |
260 (defvar gnus-cite-article nil) | |
261 | |
262 (defvar gnus-cite-prefix-alist nil) | |
263 ;; Alist of citation prefixes. | |
264 ;; The cdr is a list of lines with that prefix. | |
265 | |
266 (defvar gnus-cite-attribution-alist nil) | |
267 ;; Alist of attribution lines. | |
268 ;; The car is a line number. | |
269 ;; The cdr is the prefix for the citation started by that line. | |
270 | |
271 (defvar gnus-cite-loose-prefix-alist nil) | |
272 ;; Alist of citation prefixes that have no matching attribution. | |
273 ;; The cdr is a list of lines with that prefix. | |
274 | |
275 (defvar gnus-cite-loose-attribution-alist nil) | |
276 ;; Alist of attribution lines that have no matching citation. | |
277 ;; Each member has the form (WROTE IN PREFIX TAG), where | |
278 ;; WROTE: is the attribution line number | |
279 ;; IN: is the line number of the previous line if part of the same attribution, | |
280 ;; PREFIX: Is the citation prefix of the attribution line(s), and | |
281 ;; TAG: Is a Supercite tag, if any. | |
282 | |
283 (defvar gnus-cited-text-button-line-format-alist | |
284 `((?b (marker-position beg) ?d) | |
285 (?e (marker-position end) ?d) | |
286 (?l (- end beg) ?d))) | |
287 (defvar gnus-cited-text-button-line-format-spec nil) | |
288 | |
289 ;;; Commands: | |
290 | |
291 (defun gnus-article-highlight-citation (&optional force) | |
292 "Highlight cited text. | |
293 Each citation in the article will be highlighted with a different face. | |
294 The faces are taken from `gnus-cite-face-list'. | |
295 Attribution lines are highlighted with the same face as the | |
296 corresponding citation merged with `gnus-cite-attribution-face'. | |
297 | |
298 Text is considered cited if at least `gnus-cite-minimum-match-count' | |
299 lines matches `gnus-cite-prefix-regexp' with the same prefix. | |
300 | |
301 Lines matching `gnus-cite-attribution-suffix' and perhaps | |
302 `gnus-cite-attribution-prefix' are considered attribution lines." | |
303 (interactive (list 'force)) | |
304 (save-excursion | |
305 (set-buffer gnus-article-buffer) | |
306 (gnus-cite-parse-maybe force) | |
307 (let ((buffer-read-only nil) | |
308 (alist gnus-cite-prefix-alist) | |
309 (faces gnus-cite-face-list) | |
310 (inhibit-point-motion-hooks t) | |
311 face entry prefix skip numbers number face-alist) | |
312 ;; Loop through citation prefixes. | |
313 (while alist | |
314 (setq entry (car alist) | |
315 alist (cdr alist) | |
316 prefix (car entry) | |
317 numbers (cdr entry) | |
318 face (car faces) | |
319 faces (or (cdr faces) gnus-cite-face-list) | |
320 face-alist (cons (cons prefix face) face-alist)) | |
321 (while numbers | |
322 (setq number (car numbers) | |
323 numbers (cdr numbers)) | |
324 (and (not (assq number gnus-cite-attribution-alist)) | |
325 (not (assq number gnus-cite-loose-attribution-alist)) | |
326 (gnus-cite-add-face number prefix face)))) | |
327 ;; Loop through attribution lines. | |
328 (setq alist gnus-cite-attribution-alist) | |
329 (while alist | |
330 (setq entry (car alist) | |
331 alist (cdr alist) | |
332 number (car entry) | |
333 prefix (cdr entry) | |
334 skip (gnus-cite-find-prefix number) | |
335 face (cdr (assoc prefix face-alist))) | |
336 ;; Add attribution button. | |
337 (goto-line number) | |
338 (when (re-search-forward gnus-cite-attribution-suffix | |
339 (save-excursion (end-of-line 1) (point)) | |
340 t) | |
341 (gnus-article-add-button (match-beginning 1) (match-end 1) | |
342 'gnus-cite-toggle prefix)) | |
343 ;; Highlight attribution line. | |
344 (gnus-cite-add-face number skip face) | |
345 (gnus-cite-add-face number skip gnus-cite-attribution-face)) | |
346 ;; Loop through attribution lines. | |
347 (setq alist gnus-cite-loose-attribution-alist) | |
348 (while alist | |
349 (setq entry (car alist) | |
350 alist (cdr alist) | |
351 number (car entry) | |
352 skip (gnus-cite-find-prefix number)) | |
353 (gnus-cite-add-face number skip gnus-cite-attribution-face))))) | |
354 | |
355 (defun gnus-dissect-cited-text () | |
356 "Dissect the article buffer looking for cited text." | |
357 (save-excursion | |
358 (set-buffer gnus-article-buffer) | |
359 (gnus-cite-parse-maybe) | |
360 (let ((alist gnus-cite-prefix-alist) | |
361 prefix numbers number marks m) | |
362 ;; Loop through citation prefixes. | |
363 (while alist | |
364 (setq numbers (pop alist) | |
365 prefix (pop numbers)) | |
366 (while numbers | |
367 (setq number (pop numbers)) | |
368 (goto-char (point-min)) | |
369 (forward-line number) | |
370 (push (cons (point-marker) "") marks) | |
371 (while (and numbers | |
372 (= (1- number) (car numbers))) | |
373 (setq number (pop numbers))) | |
374 (goto-char (point-min)) | |
375 (forward-line (1- number)) | |
376 (push (cons (point-marker) prefix) marks))) | |
377 ;; Skip to the beginning of the body. | |
378 (goto-char (point-min)) | |
379 (search-forward "\n\n" nil t) | |
380 (push (cons (point-marker) "") marks) | |
381 ;; Find the end of the body. | |
382 (goto-char (point-max)) | |
383 (gnus-article-search-signature) | |
384 (push (cons (point-marker) "") marks) | |
385 ;; Sort the marks. | |
386 (setq marks (sort marks (lambda (m1 m2) (< (car m1) (car m2))))) | |
387 (let ((omarks marks)) | |
388 (setq marks nil) | |
389 (while (cdr omarks) | |
390 (if (= (caar omarks) (caadr omarks)) | |
391 (progn | |
392 (unless (equal (cdar omarks) "") | |
393 (push (car omarks) marks)) | |
394 (unless (equal (cdadr omarks) "") | |
395 (push (cadr omarks) marks)) | |
396 (unless (and (equal (cdar omarks) "") | |
397 (equal (cdadr omarks) "") | |
398 (not (cddr omarks))) | |
399 (setq omarks (cdr omarks)))) | |
400 (push (car omarks) marks)) | |
401 (setq omarks (cdr omarks))) | |
402 (when (car omarks) | |
403 (push (car omarks) marks)) | |
404 (setq marks (setq m (nreverse marks))) | |
405 (while (cddr m) | |
406 (if (and (equal (cdadr m) "") | |
407 (equal (cdar m) (cdaddr m)) | |
408 (goto-char (caadr m)) | |
409 (forward-line 1) | |
410 (= (point) (caaddr m))) | |
411 (setcdr m (cdddr m)) | |
412 (setq m (cdr m)))) | |
413 marks)))) | |
414 | |
415 (defun gnus-article-fill-cited-article (&optional force width) | |
416 "Do word wrapping in the current article. | |
417 If WIDTH (the numerical prefix), use that text width when filling." | |
418 (interactive (list t current-prefix-arg)) | |
419 (save-excursion | |
420 (set-buffer gnus-article-buffer) | |
421 (let ((buffer-read-only nil) | |
422 (inhibit-point-motion-hooks t) | |
423 (marks (gnus-dissect-cited-text)) | |
424 (adaptive-fill-mode nil) | |
425 (filladapt-mode nil) | |
426 (fill-column (if width (prefix-numeric-value width) fill-column))) | |
427 (save-restriction | |
428 (while (cdr marks) | |
429 (widen) | |
430 (narrow-to-region (caar marks) (caadr marks)) | |
431 (let ((adaptive-fill-regexp | |
432 (concat "^" (regexp-quote (cdar marks)) " *")) | |
433 (fill-prefix (cdar marks))) | |
434 (fill-region (point-min) (point-max))) | |
435 (set-marker (caar marks) nil) | |
436 (setq marks (cdr marks))) | |
437 (when marks | |
438 (set-marker (caar marks) nil)) | |
439 ;; All this information is now incorrect. | |
440 (setq gnus-cite-prefix-alist nil | |
441 gnus-cite-attribution-alist nil | |
442 gnus-cite-loose-prefix-alist nil | |
19969
5f1ab3dd344d
*** empty log message ***
Lars Magne Ingebrigtsen <larsi@gnus.org>
parents:
19521
diff
changeset
|
443 gnus-cite-loose-attribution-alist nil |
5f1ab3dd344d
*** empty log message ***
Lars Magne Ingebrigtsen <larsi@gnus.org>
parents:
19521
diff
changeset
|
444 gnus-cite-article nil))))) |
17493 | 445 |
446 (defun gnus-article-hide-citation (&optional arg force) | |
447 "Toggle hiding of all cited text except attribution lines. | |
448 See the documentation for `gnus-article-highlight-citation'. | |
449 If given a negative prefix, always show; if given a positive prefix, | |
450 always hide." | |
451 (interactive (append (gnus-article-hidden-arg) (list 'force))) | |
452 (setq gnus-cited-text-button-line-format-spec | |
453 (gnus-parse-format gnus-cited-text-button-line-format | |
454 gnus-cited-text-button-line-format-alist t)) | |
455 (save-excursion | |
456 (set-buffer gnus-article-buffer) | |
457 (cond | |
458 ((gnus-article-check-hidden-text 'cite arg) | |
459 t) | |
460 ((gnus-article-text-type-exists-p 'cite) | |
461 (let ((buffer-read-only nil)) | |
462 (gnus-article-hide-text-of-type 'cite))) | |
463 (t | |
464 (let ((buffer-read-only nil) | |
465 (marks (gnus-dissect-cited-text)) | |
466 (inhibit-point-motion-hooks t) | |
467 (props (nconc (list 'article-type 'cite) | |
468 gnus-hidden-properties)) | |
469 beg end) | |
470 (while marks | |
471 (setq beg nil | |
472 end nil) | |
473 (while (and marks (string= (cdar marks) "")) | |
474 (setq marks (cdr marks))) | |
475 (when marks | |
476 (setq beg (caar marks))) | |
477 (while (and marks (not (string= (cdar marks) ""))) | |
478 (setq marks (cdr marks))) | |
479 (when marks | |
480 (setq end (caar marks))) | |
481 ;; Skip past lines we want to leave visible. | |
482 (when (and beg end gnus-cited-lines-visible) | |
483 (goto-char beg) | |
484 (forward-line gnus-cited-lines-visible) | |
485 (if (>= (point) end) | |
486 (setq beg nil) | |
487 (setq beg (point-marker)))) | |
488 (when (and beg end) | |
489 (gnus-add-text-properties beg end props) | |
490 (goto-char beg) | |
491 (unless (save-excursion (search-backward "\n\n" nil t)) | |
492 (insert "\n")) | |
493 (put-text-property | |
494 (point) | |
495 (progn | |
496 (gnus-article-add-button | |
497 (point) | |
498 (progn (eval gnus-cited-text-button-line-format-spec) (point)) | |
499 `gnus-article-toggle-cited-text (cons beg end)) | |
500 (point)) | |
501 'article-type 'annotation) | |
502 (set-marker beg (point))))))))) | |
503 | |
504 (defun gnus-article-toggle-cited-text (region) | |
505 "Toggle hiding the text in REGION." | |
506 (let (buffer-read-only) | |
507 (funcall | |
508 (if (text-property-any | |
509 (car region) (1- (cdr region)) | |
510 (car gnus-hidden-properties) (cadr gnus-hidden-properties)) | |
511 'remove-text-properties 'gnus-add-text-properties) | |
512 (car region) (cdr region) gnus-hidden-properties))) | |
513 | |
514 (defun gnus-article-hide-citation-maybe (&optional arg force) | |
515 "Toggle hiding of cited text that has an attribution line. | |
516 If given a negative prefix, always show; if given a positive prefix, | |
517 always hide. | |
518 This will do nothing unless at least `gnus-cite-hide-percentage' | |
519 percent and at least `gnus-cite-hide-absolute' lines of the body is | |
520 cited text with attributions. When called interactively, these two | |
521 variables are ignored. | |
522 See also the documentation for `gnus-article-highlight-citation'." | |
523 (interactive (append (gnus-article-hidden-arg) (list 'force))) | |
524 (unless (gnus-article-check-hidden-text 'cite arg) | |
525 (save-excursion | |
526 (set-buffer gnus-article-buffer) | |
527 (gnus-cite-parse-maybe force) | |
528 (goto-char (point-min)) | |
529 (search-forward "\n\n" nil t) | |
530 (let ((start (point)) | |
531 (atts gnus-cite-attribution-alist) | |
532 (buffer-read-only nil) | |
533 (inhibit-point-motion-hooks t) | |
534 (hiden 0) | |
535 total) | |
536 (goto-char (point-max)) | |
537 (gnus-article-search-signature) | |
538 (setq total (count-lines start (point))) | |
539 (while atts | |
540 (setq hiden (+ hiden (length (cdr (assoc (cdar atts) | |
541 gnus-cite-prefix-alist)))) | |
542 atts (cdr atts))) | |
543 (when (or force | |
544 (and (> (* 100 hiden) (* gnus-cite-hide-percentage total)) | |
545 (> hiden gnus-cite-hide-absolute))) | |
546 (setq atts gnus-cite-attribution-alist) | |
547 (while atts | |
548 (setq total (cdr (assoc (cdar atts) gnus-cite-prefix-alist)) | |
549 atts (cdr atts)) | |
550 (while total | |
551 (setq hiden (car total) | |
552 total (cdr total)) | |
553 (goto-line hiden) | |
554 (unless (assq hiden gnus-cite-attribution-alist) | |
555 (gnus-add-text-properties | |
556 (point) (progn (forward-line 1) (point)) | |
557 (nconc (list 'article-type 'cite) | |
558 gnus-hidden-properties)))))))))) | |
559 | |
560 (defun gnus-article-hide-citation-in-followups () | |
561 "Hide cited text in non-root articles." | |
562 (interactive) | |
563 (save-excursion | |
564 (set-buffer gnus-article-buffer) | |
565 (let ((article (cdr gnus-article-current))) | |
566 (unless (save-excursion | |
567 (set-buffer gnus-summary-buffer) | |
568 (gnus-article-displayed-root-p article)) | |
569 (gnus-article-hide-citation))))) | |
570 | |
571 ;;; Internal functions: | |
572 | |
573 (defun gnus-cite-parse-maybe (&optional force) | |
574 ;; Parse if the buffer has changes since last time. | |
575 (if (equal gnus-cite-article gnus-article-current) | |
576 () | |
577 ;;Reset parser information. | |
578 (setq gnus-cite-prefix-alist nil | |
579 gnus-cite-attribution-alist nil | |
580 gnus-cite-loose-prefix-alist nil | |
581 gnus-cite-loose-attribution-alist nil) | |
582 ;; Parse if not too large. | |
583 (if (and (not force) | |
584 gnus-cite-parse-max-size | |
585 (> (buffer-size) gnus-cite-parse-max-size)) | |
586 () | |
587 (setq gnus-cite-article (cons (car gnus-article-current) | |
588 (cdr gnus-article-current))) | |
589 (gnus-cite-parse-wrapper)))) | |
590 | |
591 (defun gnus-cite-parse-wrapper () | |
592 ;; Wrap chopped gnus-cite-parse | |
593 (goto-char (point-min)) | |
594 (unless (search-forward "\n\n" nil t) | |
595 (goto-char (point-max))) | |
596 (save-excursion | |
597 (gnus-cite-parse-attributions)) | |
598 ;; Try to avoid check citation if there is no reason to believe | |
599 ;; that article has citations | |
600 (if (or gnus-cite-always-check | |
601 (save-excursion | |
602 (re-search-backward gnus-cite-reply-regexp nil t)) | |
603 gnus-cite-loose-attribution-alist) | |
604 (progn (save-excursion | |
605 (gnus-cite-parse)) | |
606 (save-excursion | |
607 (gnus-cite-connect-attributions))))) | |
608 | |
609 (defun gnus-cite-parse () | |
610 ;; Parse and connect citation prefixes and attribution lines. | |
611 | |
612 ;; Parse current buffer searching for citation prefixes. | |
613 (let ((line (1+ (count-lines (point-min) (point)))) | |
614 (case-fold-search t) | |
615 (max (save-excursion | |
616 (goto-char (point-max)) | |
617 (gnus-article-search-signature) | |
618 (point))) | |
619 alist entry start begin end numbers prefix) | |
620 ;; Get all potential prefixes in `alist'. | |
621 (while (< (point) max) | |
622 ;; Each line. | |
623 (setq begin (point) | |
624 end (progn (beginning-of-line 2) (point)) | |
625 start end) | |
626 (goto-char begin) | |
627 ;; Ignore standard Supercite attribution prefix. | |
628 (when (looking-at gnus-supercite-regexp) | |
629 (if (match-end 1) | |
630 (setq end (1+ (match-end 1))) | |
631 (setq end (1+ begin)))) | |
632 ;; Ignore very long prefixes. | |
633 (when (> end (+ (point) gnus-cite-max-prefix)) | |
634 (setq end (+ (point) gnus-cite-max-prefix))) | |
635 (while (re-search-forward gnus-cite-prefix-regexp (1- end) t) | |
636 ;; Each prefix. | |
637 (setq end (match-end 0) | |
638 prefix (buffer-substring begin end)) | |
639 (gnus-set-text-properties 0 (length prefix) nil prefix) | |
640 (setq entry (assoc prefix alist)) | |
641 (if entry | |
642 (setcdr entry (cons line (cdr entry))) | |
643 (push (list prefix line) alist)) | |
644 (goto-char begin)) | |
645 (goto-char start) | |
646 (setq line (1+ line))) | |
647 ;; We got all the potential prefixes. Now create | |
648 ;; `gnus-cite-prefix-alist' containing the oldest prefix for each | |
649 ;; line that appears at least gnus-cite-minimum-match-count | |
650 ;; times. First sort them by length. Longer is older. | |
651 (setq alist (sort alist (lambda (a b) | |
652 (> (length (car a)) (length (car b)))))) | |
653 (while alist | |
654 (setq entry (car alist) | |
655 prefix (car entry) | |
656 numbers (cdr entry) | |
657 alist (cdr alist)) | |
658 (cond ((null numbers) | |
659 ;; No lines with this prefix that wasn't also part of | |
660 ;; a longer prefix. | |
661 ) | |
662 ((< (length numbers) gnus-cite-minimum-match-count) | |
663 ;; Too few lines with this prefix. We keep it a bit | |
664 ;; longer in case it is an exact match for an attribution | |
665 ;; line, but we don't remove the line from other | |
666 ;; prefixes. | |
667 (push entry gnus-cite-prefix-alist)) | |
668 (t | |
669 (push entry | |
670 gnus-cite-prefix-alist) | |
671 ;; Remove articles from other prefixes. | |
672 (let ((loop alist) | |
673 current) | |
674 (while loop | |
675 (setq current (car loop) | |
676 loop (cdr loop)) | |
677 (setcdr current | |
678 (gnus-set-difference (cdr current) numbers))))))))) | |
679 | |
680 (defun gnus-cite-parse-attributions () | |
681 (let (al-alist) | |
682 ;; Parse attributions | |
683 (while (re-search-forward gnus-cite-attribution-suffix (point-max) t) | |
684 (let* ((start (match-beginning 0)) | |
685 (end (match-end 0)) | |
686 (wrote (count-lines (point-min) end)) | |
687 (prefix (gnus-cite-find-prefix wrote)) | |
688 ;; Check previous line for an attribution leader. | |
689 (tag (progn | |
690 (beginning-of-line 1) | |
691 (when (looking-at gnus-supercite-secondary-regexp) | |
692 (buffer-substring (match-beginning 1) | |
693 (match-end 1))))) | |
694 (in (progn | |
695 (goto-char start) | |
696 (and (re-search-backward gnus-cite-attribution-prefix | |
697 (save-excursion | |
698 (beginning-of-line 0) | |
699 (point)) | |
700 t) | |
701 (not (re-search-forward gnus-cite-attribution-suffix | |
702 start t)) | |
703 (count-lines (point-min) (1+ (point))))))) | |
704 (when (eq wrote in) | |
705 (setq in nil)) | |
706 (goto-char end) | |
707 ;; don't add duplicates | |
708 (let ((al (buffer-substring (save-excursion (beginning-of-line 0) | |
709 (1+ (point))) | |
710 end))) | |
711 (if (not (assoc al al-alist)) | |
712 (progn | |
713 (push (list wrote in prefix tag) | |
714 gnus-cite-loose-attribution-alist) | |
715 (push (cons al t) al-alist)))))))) | |
716 | |
717 (defun gnus-cite-connect-attributions () | |
718 ;; Connect attributions to citations | |
719 | |
720 ;; No citations have been connected to attribution lines yet. | |
721 (setq gnus-cite-loose-prefix-alist (append gnus-cite-prefix-alist nil)) | |
722 | |
723 ;; Parse current buffer searching for attribution lines. | |
724 ;; Find exact supercite citations. | |
725 (gnus-cite-match-attributions 'small nil | |
726 (lambda (prefix tag) | |
727 (when tag | |
728 (concat "\\`" | |
729 (regexp-quote prefix) "[ \t]*" | |
730 (regexp-quote tag) ">")))) | |
731 ;; Find loose supercite citations after attributions. | |
732 (gnus-cite-match-attributions 'small t | |
733 (lambda (prefix tag) | |
734 (when tag | |
735 (concat "\\<" | |
736 (regexp-quote tag) | |
737 "\\>")))) | |
738 ;; Find loose supercite citations anywhere. | |
739 (gnus-cite-match-attributions 'small nil | |
740 (lambda (prefix tag) | |
741 (when tag | |
742 (concat "\\<" | |
743 (regexp-quote tag) | |
744 "\\>")))) | |
745 ;; Find nested citations after attributions. | |
746 (gnus-cite-match-attributions 'small-if-unique t | |
747 (lambda (prefix tag) | |
748 (concat "\\`" (regexp-quote prefix) ".+"))) | |
749 ;; Find nested citations anywhere. | |
750 (gnus-cite-match-attributions 'small nil | |
751 (lambda (prefix tag) | |
752 (concat "\\`" (regexp-quote prefix) ".+"))) | |
753 ;; Remove loose prefixes with too few lines. | |
754 (let ((alist gnus-cite-loose-prefix-alist) | |
755 entry) | |
756 (while alist | |
757 (setq entry (car alist) | |
758 alist (cdr alist)) | |
759 (when (< (length (cdr entry)) gnus-cite-minimum-match-count) | |
760 (setq gnus-cite-prefix-alist | |
761 (delq entry gnus-cite-prefix-alist) | |
762 gnus-cite-loose-prefix-alist | |
763 (delq entry gnus-cite-loose-prefix-alist))))) | |
764 ;; Find flat attributions. | |
765 (gnus-cite-match-attributions 'first t nil) | |
766 ;; Find any attributions (are we getting desperate yet?). | |
767 (gnus-cite-match-attributions 'first nil nil)) | |
768 | |
769 (defun gnus-cite-match-attributions (sort after fun) | |
770 ;; Match all loose attributions and citations (SORT AFTER FUN) . | |
771 ;; | |
772 ;; If SORT is `small', the citation with the shortest prefix will be | |
773 ;; used, if it is `first' the first prefix will be used, if it is | |
774 ;; `small-if-unique' the shortest prefix will be used if the | |
775 ;; attribution line does not share its own prefix with other | |
776 ;; loose attribution lines, otherwise the first prefix will be used. | |
777 ;; | |
778 ;; If AFTER is non-nil, only citations after the attribution line | |
779 ;; will be considered. | |
780 ;; | |
781 ;; If FUN is non-nil, it will be called with the arguments (WROTE | |
782 ;; PREFIX TAG) and expected to return a regular expression. Only | |
783 ;; citations whose prefix matches the regular expression will be | |
784 ;; considered. | |
785 ;; | |
786 ;; WROTE is the attribution line number. | |
787 ;; PREFIX is the attribution line prefix. | |
788 ;; TAG is the Supercite tag on the attribution line. | |
789 (let ((atts gnus-cite-loose-attribution-alist) | |
790 (case-fold-search t) | |
791 att wrote in prefix tag regexp limit smallest best size) | |
792 (while atts | |
793 (setq att (car atts) | |
794 atts (cdr atts) | |
795 wrote (nth 0 att) | |
796 in (nth 1 att) | |
797 prefix (nth 2 att) | |
798 tag (nth 3 att) | |
799 regexp (if fun (funcall fun prefix tag) "") | |
800 size (cond ((eq sort 'small) t) | |
801 ((eq sort 'first) nil) | |
802 (t (< (length (gnus-cite-find-loose prefix)) 2))) | |
803 limit (if after wrote -1) | |
804 smallest 1000000 | |
805 best nil) | |
806 (let ((cites gnus-cite-loose-prefix-alist) | |
807 cite candidate numbers first compare) | |
808 (while cites | |
809 (setq cite (car cites) | |
810 cites (cdr cites) | |
811 candidate (car cite) | |
812 numbers (cdr cite) | |
813 first (apply 'min numbers) | |
814 compare (if size (length candidate) first)) | |
815 (and (> first limit) | |
816 regexp | |
817 (string-match regexp candidate) | |
818 (< compare smallest) | |
819 (setq best cite | |
820 smallest compare)))) | |
821 (if (null best) | |
822 () | |
823 (setq gnus-cite-loose-attribution-alist | |
824 (delq att gnus-cite-loose-attribution-alist)) | |
825 (push (cons wrote (car best)) gnus-cite-attribution-alist) | |
826 (when in | |
827 (push (cons in (car best)) gnus-cite-attribution-alist)) | |
828 (when (memq best gnus-cite-loose-prefix-alist) | |
829 (let ((loop gnus-cite-prefix-alist) | |
830 (numbers (cdr best)) | |
831 current) | |
832 (setq gnus-cite-loose-prefix-alist | |
833 (delq best gnus-cite-loose-prefix-alist)) | |
834 (while loop | |
835 (setq current (car loop) | |
836 loop (cdr loop)) | |
837 (if (eq current best) | |
838 () | |
839 (setcdr current (gnus-set-difference (cdr current) numbers)) | |
840 (when (null (cdr current)) | |
841 (setq gnus-cite-loose-prefix-alist | |
842 (delq current gnus-cite-loose-prefix-alist) | |
843 atts (delq current atts))))))))))) | |
844 | |
845 (defun gnus-cite-find-loose (prefix) | |
846 ;; Return a list of loose attribution lines prefixed by PREFIX. | |
847 (let* ((atts gnus-cite-loose-attribution-alist) | |
848 att line lines) | |
849 (while atts | |
850 (setq att (car atts) | |
851 line (car att) | |
852 atts (cdr atts)) | |
853 (when (string-equal (gnus-cite-find-prefix line) prefix) | |
854 (push line lines))) | |
855 lines)) | |
856 | |
857 (defun gnus-cite-add-face (number prefix face) | |
858 ;; At line NUMBER, ignore PREFIX and add FACE to the rest of the line. | |
859 (when face | |
860 (let ((inhibit-point-motion-hooks t) | |
861 from to) | |
862 (goto-line number) | |
863 (unless (eobp);; Sometimes things become confused. | |
864 (forward-char (length prefix)) | |
865 (skip-chars-forward " \t") | |
866 (setq from (point)) | |
867 (end-of-line 1) | |
868 (skip-chars-backward " \t") | |
869 (setq to (point)) | |
870 (when (< from to) | |
871 (gnus-overlay-put (gnus-make-overlay from to) 'face face)))))) | |
872 | |
873 (defun gnus-cite-toggle (prefix) | |
874 (save-excursion | |
875 (set-buffer gnus-article-buffer) | |
876 (let ((buffer-read-only nil) | |
877 (numbers (cdr (assoc prefix gnus-cite-prefix-alist))) | |
878 (inhibit-point-motion-hooks t) | |
879 number) | |
880 (while numbers | |
881 (setq number (car numbers) | |
882 numbers (cdr numbers)) | |
883 (goto-line number) | |
884 (cond ((get-text-property (point) 'invisible) | |
885 (remove-text-properties (point) (progn (forward-line 1) (point)) | |
886 gnus-hidden-properties)) | |
887 ((assq number gnus-cite-attribution-alist)) | |
888 (t | |
889 (gnus-add-text-properties | |
890 (point) (progn (forward-line 1) (point)) | |
891 (nconc (list 'article-type 'cite) | |
892 gnus-hidden-properties)))))))) | |
893 | |
894 (defun gnus-cite-find-prefix (line) | |
895 ;; Return citation prefix for LINE. | |
896 (let ((alist gnus-cite-prefix-alist) | |
897 (prefix "") | |
898 entry) | |
899 (while alist | |
900 (setq entry (car alist) | |
901 alist (cdr alist)) | |
902 (when (memq line (cdr entry)) | |
903 (setq prefix (car entry)))) | |
904 prefix)) | |
905 | |
906 (gnus-add-shutdown 'gnus-cache-close 'gnus) | |
907 | |
908 (defun gnus-cache-close () | |
909 (setq gnus-cite-prefix-alist nil)) | |
910 | |
911 (gnus-ems-redefine) | |
912 | |
913 (provide 'gnus-cite) | |
914 | |
915 ;;; gnus-cite.el ends here |