*** empty log message ***
[emacs.git] / lisp / superyank.el
blob9746b22a4501a0b38fd998b86c22e0826b0b6ec0
1 ;;; superyank.el --- smart message-yanking code for GNUS
3 ;; Author: Barry A. Warsaw <warsaw@cme.nist.gov>
4 ;; Version: 1.1
5 ;; Last-Modified: 15 Jul 1992
6 ;; Adapted-By: ESR
8 ;;; Commentary:
10 ;; Inserts the message being replied to with various user controlled
11 ;; citation styles.
14 ;; This file is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY. No author or distributor
16 ;; accepts responsibility to anyone for the consequences of using it
17 ;; or for whether it serves any particular purpose or works at all,
18 ;; unless he says so in writing. Refer to the GNU Emacs General Public
19 ;; License for full details.
21 ;; Everyone is granted permission to copy, modify and redistribute
22 ;; this file, but only under the conditions described in the
23 ;; GNU Emacs General Public License. A copy of this license is
24 ;; supposed to have been given to you along with GNU Emacs so you
25 ;; can know your rights and responsibilities. It should be in a
26 ;; file named COPYING. Among other things, the copyright notice
27 ;; and this notice must be preserved on all copies.
29 ;; NAME: Barry A. Warsaw USMAIL: National Institute of Standards
30 ;; TELE: (301) 975-3460 and Technology (formerly NBS)
31 ;; UUCP: {...}!uunet!cme-durer!warsaw Rm. B-124, Bldg. 220
32 ;; ARPA: warsaw@cme.nist.gov Gaithersburg, MD 20899
34 ;; Modification history:
36 ;; modified: 14-Jun-1989 baw (better keymap set procedure, rewrite-headers)
37 ;; modified: 12-Jun-1989 baw (added defvar for sy-use-only-preference-p)
38 ;; modified: 6-Jun-1989 baw (better sy-rewrite-headers, no kill/yank)
39 ;; modified: 5-Jun-1989 baw (requires rnewspost.el)
40 ;; modified: 1-Jun-1989 baw (persistent attribution, sy-open-line)
41 ;; modified: 31-May-1989 baw (fixed some gnus problems, id'd another)
42 ;; modified: 22-May-1989 baw (documentation)
43 ;; modified: 8-May-1989 baw (auto filling of regions)
44 ;; modified: 1-May-1989 baw (documentation)
45 ;; modified: 27-Apr-1989 baw (new preference scheme)
46 ;; modified: 24-Apr-1989 baw (remove gnus headers, attrib scheme, cite lines)
47 ;; modified: 19-Apr-1989 baw (cite key, fill p, yank region, naming scheme)
48 ;; modified: 12-Apr-1989 baw (incorp other mail yank features seen on net)
49 ;; created : 16-Feb-1989 baw (mod vanilla fn indent-rigidly mail-yank-original)
51 ;; Though I wrote this package basically from scratch, as an elisp
52 ;; learning exercise, it was inspired by postings of similar packages to
53 ;; the gnu.emacs newsgroup over the past month or so.
55 ;; Here's a brief history of how this package developed:
57 ;; I as well as others on the net were pretty unhappy about the way emacs
58 ;; cited replies with the tab or 4 spaces. It looked ugly and made it hard
59 ;; to distinguish between original and cited lines. I hacked on the function
60 ;; yank-original to at least give the user the ability to define the citation
61 ;; character. I posted this simple hack, and others did as well. The main
62 ;; difference between mine and others was that a space was put after the
63 ;; citation string on on new citations, but not after previously cited lines:
65 ;; >> John wrote this originally
66 ;; > Jane replied to that
68 ;; Then Martin Neitzel posted some code that he developed, derived in part
69 ;; from code that Ashwin Ram posted previous to that. In Martin's
70 ;; posting, he introduced a new, and (IMHO) superior, citation style,
71 ;; eliminating nested citations. Yes, I wanted to join the Small-But-
72 ;; Growing-Help-Stamp-Out-Nested-Citation-Movement! You should too.
74 ;; But Martin's code simply asks the user for the citation string (here
75 ;; after called the `attribution' string), and I got to thinking, it wouldn't
76 ;; be that difficult to automate that part. So I started hacking this out.
77 ;; It proved to be not as simple as I first thought. But anyway here it
78 ;; is. See the wish list below for future plans (if I have time).
80 ;; Type "C-h f mail-yank-original" after this package is loaded to get a
81 ;; description of what it does and the variables that control it.
83 ;; ======================================================================
85 ;; Changes wish list
87 ;; 1) C-x C-s yanks a region from the RMAIL buffer instead of the
88 ;; whole buffer
90 ;; 2) reparse nested citations to try to recast as non-nested citations
91 ;; perhaps by checking the References: line
94 ;;; Code:
96 ;; ======================================================================
98 ;; require and provide features
100 (require 'sendmail)
102 ;; ======================================================================
104 ;; don't need rnewspost.el to rewrite the header. This only works
105 ;; with diffs to rnewspost.el that I posted with the original
106 ;; superyank code.
108 (setq news-reply-header-hook nil)
110 ;; **********************************************************************
111 ;; start of user defined variables
112 ;; **********************************************************************
114 ;; this section defines variables that control the operation of
115 ;; super-mail-yank. Most of these are described in the comment section
116 ;; as well as the DOCSTRING.
120 ;; ----------------------------------------------------------------------
122 ;; this variable holds the default author's name for citations
124 (defvar sy-default-attribution "Anon"
125 "String that describes attribution to unknown person. This string
126 should not contain the citation string.")
129 ;; ----------------------------------------------------------------------
131 ;; string used as an end delimiter for both nested and non-nested citations
133 (defvar sy-citation-string ">"
134 "String to use as an end-delimiter for citations. This string is
135 used in both nested and non-nested citations. For best results, use a
136 single character with no trailing space. Most commonly used string
137 is: \">\.")
140 ;; ----------------------------------------------------------------------
142 ;; variable controlling citation type, nested or non-nested
144 (defvar sy-nested-citation-p nil
145 "Non-nil uses nested citations, nil uses non-nested citations.
146 Nested citations are of the style:
148 I wrote this
149 > He wrote this
150 >> She replied to something he wrote
152 Non-nested citations are of the style:
154 I wrote this
155 John> He wrote this
156 Jane> She originally wrote this")
160 ;; ----------------------------------------------------------------------
162 ;; regular expression that matches existing citations
164 (defvar sy-cite-regexp "[a-zA-Z0-9]*>"
165 "Regular expression that describes how an already cited line in an
166 article begins. The regexp is only used at the beginning of a line,
167 so it doesn't need to begin with a '^'.")
170 ;; ----------------------------------------------------------------------
172 ;; regular expression that delimits names from titles in the field that
173 ;; looks like: (John X. Doe -- Computer Hacker Extraordinaire)
175 (defvar sy-titlecue-regexp "\\s +-+\\s +"
177 "Regular expression that delineates names from titles in the name
178 field. Often, people will set up their name field to look like this:
180 (John Xavier Doe -- Computer Hacker Extraordinaire)
182 Set to nil to treat entire field as a name.")
185 ;; ----------------------------------------------------------------------
188 (defvar sy-preferred-attribution 2
190 "This is an integer indicating what the user's preference is in
191 attribution style, based on the following key:
193 0: email address name is preferred
194 1: initials are preferred
195 2: first name is preferred
196 3: last name is preferred
198 The value of this variable may also be greater than 3, which would
199 allow you to prefer the 2nd through nth - 1 name. If the preferred
200 attribution is nil or the empty string, then the secondary preferrence
201 will be the first name. After that, the entire name alist is search
202 until a non-empty, non-nil name is found. If no such name is found,
203 then the user is either queried or the default attribution string is
204 used depending on the value of sy-confirm-always-p.
206 Examples:
208 assume the from: line looks like this:
210 from: doe@computer.some.where.com (John Xavier Doe)
212 The following preferences would return these strings:
214 0: \"doe\"
215 1: \"JXD\"
216 2: \"John\"
217 3: \"Doe\"
218 4: \"Xavier\"
220 anything else would return \"John\".")
223 ;; ----------------------------------------------------------------------
225 (defvar sy-confirm-always-p t
226 "If t, always confirm attribution string before inserting into
227 buffer.")
231 ;; ----------------------------------------------------------------------
233 ;; informative header hook
235 (defvar sy-rewrite-header-hook 'sy-header-on-said
236 "Hook for inserting informative header at the top of the yanked
237 message. Set to nil for no header. Here is a list of predefined
238 header styles; you can use these as a model to write you own:
240 sy-header-on-said [default]: On 14-Jun-1989 GMT,
241 John Xavier Doe said:
243 sy-header-inarticle-writes: In article <123456789> John Xavier Doe writes:
245 sy-header-regarding-writes: Regarding RE: superyank; John Xavier Doe adds:
247 sy-header-verbose: On 14-Jun-1989 GMT, John Xavier Doe
248 from the organization Great Company
249 has this to say about article <123456789>
250 in newsgroups misc.misc
251 concerning RE: superyank
252 referring to previous articles <987654321>
254 You can use the following variables as information strings in your header:
256 sy-reply-yank-date: the date field [ex: 14-Jun-1989 GMT]
257 sy-reply-yank-from: the from field [ex: John Xavier Doe]
258 sy-reply-yank-message-id: the message id [ex: <123456789>]
259 sy-reply-yank-subject: the subject line [ex: RE: superyank]
260 sy-reply-yank-newsgroup: the newsgroup name for GNUS [ex: misc.misc]
261 sy-reply-yank-references: the article references [ex: <987654321>]
262 sy-reply-yank-organization: the author's organization [ex: Great Company]
264 If a field can't be found, because it doesn't exist or is not being
265 shown, perhaps because of toggle-headers, the corresponding field
266 variable will contain the string \"mumble mumble\".")
269 ;; ----------------------------------------------------------------------
271 ;; non-nil means downcase the author's name string
273 (defvar sy-downcase-p nil
274 "Non-nil means downcase the author's name string.")
277 ;; ----------------------------------------------------------------------
279 ;; controls removal of leading white spaces
281 (defvar sy-left-justify-p nil
282 "If non-nil, delete all leading white space before citing.")
285 ;; ----------------------------------------------------------------------
287 ;; controls auto filling of region
289 (defvar sy-auto-fill-region-p nil
290 "If non-nil, automatically fill each paragraph that is cited. If
291 nil, do not auto fill each paragraph.")
295 ;; ----------------------------------------------------------------------
297 ;; controls use of preferred attribution only, or use of attribution search
298 ;; scheme if the preferred attrib can't be found.
300 (defvar sy-use-only-preference-p nil
302 "If non-nil, then only the preferred attribution string will be
303 used. If the preferred attribution string can not be found, then the
304 sy-default-attribution will be used. If nil, and the preferred
305 attribution string is not found, then some secondary scheme will be
306 employed to find a suitable attribution string.")
308 ;; **********************************************************************
309 ;; end of user defined variables
310 ;; **********************************************************************
313 ;; ----------------------------------------------------------------------
315 ;; The new citation style means we can clean out other headers in addition
316 ;; to those previously cleaned out. Anyway, we create our own headers.
317 ;; Also, we want to clean out any headers that gnus puts in. Add to this
318 ;; for other mail or news readers you may be using.
320 (setq mail-yank-ignored-headers "^via:\\|^origin:\\|^status:\\|^re\\(mail\\|ceiv\\)ed\\|^[a-z-]*message-id:\\|^\\(summary-\\)?line[s]?:\\|^cc:\\|^subject:\\|^\\(\\(in-\\)?reply-\\)?to:\\|^\\(\\(return\\|reply\\)-\\)?path:\\|^\\(posted-\\)?date:\\|^\\(mail-\\)?from:\\|^newsgroup[s]?:\\|^organization:\\|^keywords:\\|^distribution:\\|^references:")
323 ;; ----------------------------------------------------------------------
325 ;; global variables, not user accessable
327 (setq sy-persist-attribution (concat sy-default-attribution "> "))
328 (setq sy-reply-yank-date "")
329 (setq sy-reply-yank-from "")
330 (setq sy-reply-yank-message-id "")
331 (setq sy-reply-yank-subject "")
332 (setq sy-reply-yank-newsgroups "")
333 (setq sy-reply-yank-references "")
334 (setq sy-reply-yank-organization "")
337 ;; ======================================================================
339 ;; This section contains primitive functions used in the schemes. They
340 ;; extract name fields from various parts of the "from:" field based on
341 ;; the control variables described above.
343 ;; Some will use recursion to pick out the correct namefield in the namestring
344 ;; or the list of initials. These functions all scan a string that contains
345 ;; the name, ie: "John Xavier Doe". There is no limit on the number of names
346 ;; in the string. Also note that all white spaces are basically ignored and
347 ;; are stripped from the returned strings, and titles are ignored if
348 ;; sy-titlecue-regexp is set to non-nil.
350 ;; Others will use methods to try to extract the name from the email
351 ;; address of the originator. The types of addresses readable are
352 ;; described above.
355 ;; ----------------------------------------------------------------------
357 ;; try to extract the name from an email address of the form
358 ;; name%[stuff]
360 ;; Unlike the get-name functions above, these functions operate on the
361 ;; buffer instead of a supplied name-string.
363 (defun sy-%-style-address ()
364 (beginning-of-line)
365 (buffer-substring
366 (progn (re-search-forward "%" (point-max) t)
367 (if (not (bolp)) (forward-char -1))
368 (point))
369 (progn (re-search-backward "^\\|[^a-zA-Z0-9]")
370 (point))))
373 ;; ----------------------------------------------------------------------
375 ;; try to extract names from addresses with the form:
376 ;; [stuff]name@[stuff]
378 (defun sy-@-style-address ()
379 (beginning-of-line)
380 (buffer-substring
381 (progn (re-search-forward "@" (point-max) t)
382 (if (not (bolp)) (forward-char -1))
383 (point))
384 (progn (re-search-backward "^\\|[^a-zA-Z0-0]")
385 (if (not (bolp)) (forward-char 1))
386 (point))))
389 ;; ----------------------------------------------------------------------
391 ;; try to extract the name from addresses with the form:
392 ;; [stuff]![stuff]...!name[stuff]
394 (defun sy-!-style-address ()
395 (beginning-of-line)
396 (buffer-substring
397 (progn (while (re-search-forward "!" (point-max) t))
398 (point))
399 (progn (re-search-forward "[^a-zA-Z0-9]\\|$")
400 (if (not (eolp)) (forward-char -1))
401 (point))))
404 ;; ----------------------------------------------------------------------
406 ;; using the different email name schemes, try each one until you get a
407 ;; non-nil entry
409 (defun sy-get-emailname ()
410 (let ((en1 (sy-%-style-address))
411 (en2 (sy-@-style-address))
412 (en3 (sy-!-style-address)))
413 (cond
414 ((not (string-equal en1 "")) en1)
415 ((not (string-equal en2 "")) en2)
416 ((not (string-equal en3 "")) en3)
417 (t ""))))
420 ;; ----------------------------------------------------------------------
422 ;; returns the "car" of the namestring, really the first namefield
424 ;; (sy-string-car "John Xavier Doe")
425 ;; => "John"
427 (defun sy-string-car (namestring)
428 (substring namestring
429 (progn (string-match "\\s *" namestring) (match-end 0))
430 (progn (string-match "\\s *\\S +" namestring) (match-end 0))))
433 ;; ----------------------------------------------------------------------
435 ;; returns the "cdr" of the namestring, really the whole string from
436 ;; after the first name field to the end of the string.
438 ;; (sy-string-cdr "John Xavier Doe")
439 ;; => "Xavier Doe"
441 (defun sy-string-cdr (namestring)
442 (substring namestring
443 (progn (string-match "\\s *\\S +\\s *" namestring)
444 (match-end 0))))
447 ;; ----------------------------------------------------------------------
449 ;; convert a namestring to a list of namefields
451 ;; (sy-namestring-to-list "John Xavier Doe")
452 ;; => ("John" "Xavier" "Doe")
454 (defun sy-namestring-to-list (namestring)
455 (if (not (string-match namestring ""))
456 (append (list (sy-string-car namestring))
457 (sy-namestring-to-list (sy-string-cdr namestring)))))
460 ;; ----------------------------------------------------------------------
462 ;; strip the initials from each item in the list and return a string
463 ;; that is the concatenation of the initials
465 (defun sy-strip-initials (raw-nlist)
466 (if (not raw-nlist)
468 (concat (substring (car raw-nlist) 0 1)
469 (sy-strip-initials (cdr raw-nlist)))))
473 ;; ----------------------------------------------------------------------
475 ;; using the namestring, build a list which is in the following order
477 ;; (email, initials, firstname, lastname, name1, name2, name3 ... nameN-1)
479 (defun sy-build-ordered-namelist (namestring)
480 (let* ((raw-nlist (sy-namestring-to-list namestring))
481 (initials (sy-strip-initials raw-nlist))
482 (firstname (car raw-nlist))
483 (revnames (reverse (cdr raw-nlist)))
484 (lastname (car revnames))
485 (midnames (reverse (cdr revnames)))
486 (emailnames (sy-get-emailname)))
487 (append (list emailnames)
488 (list initials)
489 (list firstname)
490 (list lastname)
491 midnames)))
494 ;; ----------------------------------------------------------------------
496 ;; Query the user for the attribution string. Supply sy-default-attribution
497 ;; as the default choice.
499 (defun sy-query-for-attribution ()
500 (concat
501 (let* ((prompt (concat "Enter attribution string: (default "
502 sy-default-attribution
503 ") "))
504 (query (read-input prompt))
505 (attribution (if (string-equal query "")
506 sy-default-attribution
507 query)))
508 (if sy-downcase-p
509 (downcase attribution)
510 attribution))
511 sy-citation-string))
515 ;; ----------------------------------------------------------------------
517 ;; parse the current line for the namestring
519 (defun sy-get-namestring ()
520 (save-restriction
521 (beginning-of-line)
522 (if (re-search-forward "(.*)" (point-max) t)
523 (let ((start (progn
524 (beginning-of-line)
525 (re-search-forward "\\((\\s *\\)\\|$" (point-max) t)
526 (point)))
527 (end (progn
528 (re-search-forward
529 (concat "\\(\\s *\\()\\|" sy-titlecue-regexp "\\)\\)\\|$")
530 (point-max) t)
531 (point))))
532 (narrow-to-region start end)
533 (let ((start (progn
534 (beginning-of-line)
535 (point)))
536 (end (progn
537 (end-of-line)
538 (re-search-backward
539 (concat "\\s *\\()\\|" sy-titlecue-regexp "\\)$")
540 (point-min) t)
541 (point))))
542 (buffer-substring start end)))
543 (let ((start (progn
544 (beginning-of-line)
545 (re-search-forward "^\"*")
546 (point)))
547 (end (progn
548 (re-search-forward "\\(\\s *[a-zA-Z0-9\\.]+\\)*"
549 (point-max) t)
550 (point))))
551 (buffer-substring start end)))))
555 ;; ----------------------------------------------------------------------
557 ;; scan the nlist and return the integer pointing to the first legal
558 ;; non-empty namestring. Returns the integer pointing to the index
559 ;; in the nlist of the preferred namestring, or nil if no legal
560 ;; non-empty namestring could be found.
562 (defun sy-return-preference-n (nlist)
563 (let ((p sy-preferred-attribution)
564 (exception nil))
566 ;; check to be sure the index is not out-of-bounds
568 (cond
569 ((< p 0) (setq p 2) (setq exception t))
570 ((not (nth p nlist)) (setq p 2) (setq exception t)))
572 ;; check to be sure that the explicit preference is not empty
574 (if (string-equal (nth p nlist) "")
575 (progn (setq p 0)
576 (setq exception t)))
578 ;; find the first non-empty namestring
580 (while (and (nth p nlist)
581 (string-equal (nth p nlist) ""))
582 (setq exception t)
583 (setq p (+ p 1)))
585 ;; return the preference index if non-nil, otherwise nil
587 (if (or (and exception sy-use-only-preference-p)
588 (not (nth p nlist)))
590 p)))
594 ;; ----------------------------------------------------------------------
596 ;; rebuild the nlist into an alist for completing-read. Use as a guide
597 ;; the index of the preferred name field. Get the actual preferred
598 ;; name field base on other factors (see above). If no actual preferred
599 ;; name field is found, then query the user for the attribution string.
601 ;; also note that the nlist is guaranteed to be non-empty. At the very
602 ;; least it will consist of 4 empty strings ("" "" "" "")
604 (defun sy-nlist-to-alist (nlist)
605 (let ((preference (sy-return-preference-n nlist))
606 alist
607 (n 0))
609 ;; check to be sure preference is not nil
611 (if (not preference)
612 (setq alist (list (cons (sy-query-for-attribution) nil)))
614 ;; preference is non-nil
616 (setq alist (list (cons (nth preference nlist) nil)))
617 (while (nth n nlist)
618 (if (= n preference) nil
619 (setq alist (append alist (list (cons (nth n nlist) nil)))))
620 (setq n (+ n 1))))
621 alist))
626 ;; ----------------------------------------------------------------------
628 ;; confirm if desired after the alist has been built
630 (defun sy-get-attribution (alist)
631 (concat
633 ;; check to see if nested citations are to be used
635 (if sy-nested-citation-p
638 ;; check to see if confirmation is needed
639 ;; if not, just return the preference (first element in alist)
641 (if (not sy-confirm-always-p)
642 (car (car alist))
644 ;; confirmation is requested so build the prompt, confirm
645 ;; and return the chosen string
647 (let* (ignore
648 (prompt (concat "Complete attribution string: (default "
649 (car (car alist))
650 ") "))
652 ;; set up the local completion keymap
654 (minibuffer-local-must-match-map
655 (let ((map (make-sparse-keymap)))
656 (define-key map "?" 'minibuffer-completion-help)
657 (define-key map " " 'minibuffer-complete-word)
658 (define-key map "\t" 'minibuffer-complete)
659 (define-key map "\00A" 'exit-minibuffer)
660 (define-key map "\00D" 'exit-minibuffer)
661 (define-key map "\007"
662 '(lambda ()
663 (interactive)
664 (beep)
665 (exit-minibuffer)))
666 map))
668 ;; read the completion
670 (attribution (completing-read prompt alist))
672 ;; check attribution string for emptyness
674 (choice (if (or (not attribution)
675 (string-equal attribution ""))
676 (car (car alist))
677 attribution)))
679 (if sy-downcase-p
680 (downcase choice)
681 choice))))
682 sy-citation-string))
686 ;; ----------------------------------------------------------------------
688 ;; this function will scan the current rmail buffer, narrowing it to the
689 ;; from: line, then using this, it will try to decipher some names from
690 ;; that line. It will then build the name alist and try to confirm
691 ;; its choice of attribution strings. It returns the chosen attribution
692 ;; string.
694 (defun sy-scan-rmail-for-names (rmailbuffer)
695 (save-excursion
696 (let ((case-fold-search t)
697 alist
698 attribution)
699 (switch-to-buffer rmailbuffer)
700 (goto-char (point-min))
702 ;; be sure there is a from: line
704 (if (not (re-search-forward "^from:\\s *" (point-max) t))
705 (setq attribution (sy-query-for-attribution))
707 ;; if there is a from: line, then scan the narrow the buffer,
708 ;; grab the namestring, and build the alist, then using this
709 ;; get the attribution string.
711 (save-restriction
712 (narrow-to-region (point)
713 (progn (end-of-line) (point)))
714 (let* ((namestring (sy-get-namestring))
715 (nlist (sy-build-ordered-namelist namestring)))
716 (setq alist (sy-nlist-to-alist nlist))))
718 ;; we've built the alist, now confirm the attribution choice
719 ;; if appropriate
721 (setq attribution (sy-get-attribution alist)))
722 attribution)))
726 ;; ======================================================================
728 ;; the following function insert of citations, writing of headers, filling
729 ;; paragraphs and general higher level operations
733 ;; ----------------------------------------------------------------------
735 ;; insert a nested citation
737 (defun sy-insert-citation (start end cite-string)
738 (save-excursion
739 (goto-char end)
740 (setq end (point-marker))
741 (goto-char start)
742 (or (bolp)
743 (forward-line 1))
745 (let ((fill-prefix (concat cite-string " "))
746 (fstart (point))
747 (fend (point)))
749 (while (< (point) end)
751 ;; remove leading tabs if desired
753 (if sy-left-justify-p
754 (delete-region (point)
755 (progn (skip-chars-forward " \t") (point))))
757 ;; check to see if the current line should be cited
759 (if (or (eolp)
760 (looking-at sy-cite-regexp))
762 ;; do not cite this line unless nested-citations are to be
763 ;; used
765 (progn
766 (or (eolp)
767 (if sy-nested-citation-p
768 (insert cite-string)))
770 ;; set fill start and end points
772 (or (= fstart fend)
773 (not sy-auto-fill-region-p)
774 (progn (goto-char fend)
775 (or (not (eolp))
776 (setq fend (+ fend 1)))
777 (fill-region-as-paragraph fstart fend)))
778 (setq fstart (point))
779 (setq fend (point)))
781 ;; else
783 (insert fill-prefix)
784 (end-of-line)
785 (setq fend (point)))
787 (forward-line 1)))
788 (move-marker end nil)))
791 ;; ----------------------------------------------------------------------
793 ;; yank a particular field into a holding variable
795 (defun sy-yank-fields (start)
796 (save-excursion
797 (goto-char start)
798 (setq sy-reply-yank-date (mail-fetch-field "date")
799 sy-reply-yank-from (mail-fetch-field "from")
800 sy-reply-yank-subject (mail-fetch-field "subject")
801 sy-reply-yank-newsgroups (mail-fetch-field "newsgroups")
802 sy-reply-yank-references (mail-fetch-field "references")
803 sy-reply-yank-message-id (mail-fetch-field "message-id")
804 sy-reply-yank-organization (mail-fetch-field "organization"))
805 (or sy-reply-yank-date
806 (setq sy-reply-yank-date "mumble mumble"))
807 (or sy-reply-yank-from
808 (setq sy-reply-yank-from "mumble mumble"))
809 (or sy-reply-yank-subject
810 (setq sy-reply-yank-subject "mumble mumble"))
811 (or sy-reply-yank-newsgroups
812 (setq sy-reply-yank-newsgroups "mumble mumble"))
813 (or sy-reply-yank-references
814 (setq sy-reply-yank-references "mumble mumble"))
815 (or sy-reply-yank-message-id
816 (setq sy-reply-yank-message-id "mumble mumble"))
817 (or sy-reply-yank-organization
818 (setq sy-reply-yank-organization "mumble mumble"))))
821 ;; ----------------------------------------------------------------------
823 ;; rewrite the header to be more conversational
825 (defun sy-rewrite-headers (start)
826 (goto-char start)
827 (run-hooks 'sy-rewrite-header-hook))
830 ;; ----------------------------------------------------------------------
832 ;; some different styles of headers
834 (defun sy-header-on-said ()
835 (insert-string "\nOn " sy-reply-yank-date ",\n"
836 sy-reply-yank-from " said:\n"))
838 (defun sy-header-inarticle-writes ()
839 (insert-string "\nIn article " sy-reply-yank-message-id
840 " " sy-reply-yank-from " writes:\n"))
842 (defun sy-header-regarding-writes ()
843 (insert-string "\nRegarding " sy-reply-yank-subject
844 "; " sy-reply-yank-from " adds:\n"))
846 (defun sy-header-verbose ()
847 (insert-string "\nOn " sy-reply-yank-date ",\n"
848 sy-reply-yank-from "\nfrom the organization "
849 sy-reply-yank-organization "\nhad this to say about article "
850 sy-reply-yank-message-id "\nin newsgroups "
851 sy-reply-yank-newsgroups "\nconcerning "
852 sy-reply-yank-subject "\nreferring to previous articles "
853 sy-reply-yank-references "\n"))
856 ;; ----------------------------------------------------------------------
858 ;; yank the original article in and attribute
860 (defun sy-yank-original (arg)
862 "Insert the message being replied to, if any (in rmail/gnus). Puts
863 point before the text and mark after. Calls generalized citation
864 function sy-insert-citation to cite all allowable lines."
866 (interactive "P")
867 (if mail-reply-buffer
868 (let* ((sy-confirm-always-p (if (consp arg)
870 sy-confirm-always-p))
871 (attribution (sy-scan-rmail-for-names mail-reply-buffer))
872 (top (point))
873 (start (point))
874 (end (progn (delete-windows-on mail-reply-buffer)
875 (insert-buffer mail-reply-buffer)
876 (mark))))
878 (sy-yank-fields start)
879 (sy-rewrite-headers start)
880 (setq start (point))
881 (mail-yank-clear-headers top (mark))
882 (setq sy-persist-attribution (concat attribution " "))
883 (sy-insert-citation start end attribution))
885 (goto-char top)
886 (exchange-point-and-mark)))
890 ;; ----------------------------------------------------------------------
892 ;; this is here for compatibility with existing mail/news yankers
893 ;; overloads the default mail-yank-original
895 (defun mail-yank-original (arg)
897 "Yank original message buffer into the reply buffer, citing as per
898 user preferences. Numeric Argument forces confirmation.
900 Here is a description of the superyank.el package, what it does and
901 what variables control its operation. This was written by Barry
902 Warsaw (warsaw@cme.nist.gov, {...}!uunet!cme-durer!warsaw).
904 A 'Citation' is the acknowledgement of the original author of a mail
905 message. There are two general forms of citation. In 'nested
906 citations', indication is made that the cited line was written by
907 someone *other* that the current message author (or by that author at
908 an earlier time). No indication is made as to the identity of the
909 original author. Thus, a nested citation after multiple replies would
910 look like this (this is after my reply to a previous message):
912 >>John originally wrote this
913 >>and this as well
914 > Jane said that John didn't know
915 > what he was talking about
916 And that's what I think as well.
918 In non-nested citations, you won't see multiple \">\" characters at
919 the beginning of the line. Non-nested citations will insert an
920 informative string at the beginning of a cited line, attributing that
921 line to an author. The same message described above might look like
922 this if non-nested citations were used:
924 John> John originally wrote this
925 John> and this as well
926 Jane> Jane said that John didn't know
927 Jane> what he was talking about
928 And that's what I think as well.
930 Notice that my inclusion of Jane's inclusion of John's original
931 message did not result in a cited line of the form: Jane>John>. Thus
932 no nested citations. The style of citation is controlled by the
933 variable `sy-nested-citation-p'. Nil uses non-nested citations and
934 non-nil uses old style, nested citations.
936 The variable `sy-citation-string' is the string to use as a marker for
937 a citation, either nested or non-nested. For best results, this
938 string should be a single character with no trailing space and is
939 typically the character \">\". In non-nested citations this string is
940 appended to the attribution string (author's name), along with a
941 trailing space. In nested citations, a trailing space is only added
942 to a first level citation.
944 Another important variable is `sy-cite-regexp' which describes strings
945 that indicate a previously cited line. This regular expression is
946 always used at the beginning of a line so it doesn't need to begin
947 with a \"^\" character. Change this variable if you change
948 `sy-citation-string'.
950 The following section only applies to non-nested citations.
952 This package has a fair amount of intellegence related to deciphering
953 the author's name based on information provided by the original
954 message buffer. In normal operation, the program will pick out the
955 author's first and last names, initials, terminal email address and
956 any other names it can find. It will then pick an attribution string
957 from this list based on a user defined preference and it will ask for
958 confirmation if the user specifies. This package gathers its
959 information from the `From:' line of the original message buffer. It
960 recognizes From: lines with the following forms:
962 From: John Xavier Doe <doe@speedy.computer.com>
963 From: \"John Xavier Doe\" <doe@speedy.computer.com>
964 From: doe@speedy.computer.com (John Xavier Doe)
965 From: computer!speedy!doe (John Xavier Doe)
966 From: computer!speedy!doe (John Xavier Doe)
967 From: doe%speedy@computer.com (John Xavier Doe)
969 In this case, if confirmation is requested, the following strings will
970 be made available for completion and confirmation:
972 \"John\"
973 \"Xavier\"
974 \"Doe\"
975 \"JXD\"
976 \"doe\"
978 Note that completion is case sensitive. If there was a problem
979 picking out a From: line, or any other problem getting even a single
980 name, then the user will be queried for an attribution string. The
981 default attribution string is set in the variable
982 `sy-default-attribution'.
984 Sometimes people set their name fields so that it also includes a
985 title of the form:
987 From: doe@speedy.computer.com (John Doe -- Hacker Extraordinaire)
989 To avoid the inclusion of the string \"-- Hacker Extraordinaire\" in
990 the name list, the variable `sy-titlecue-regexp' is provided. Its
991 default setting will still properly recognize names of the form:
993 From: xdoe@speedy.computer.com (John Xavier-Doe -- Crazed Hacker)
995 The variable `sy-preferred-attribution' contains an integer that
996 indicates which name field the user prefers to use as the attribution
997 string, based on the following key:
999 0: email address name is preferred
1000 1: initials are preferred
1001 2: first name is preferred
1002 3: last name is preferred
1004 The value can be greater than 3, in which case, you would be
1005 preferring the 2nd throught nth -1 name. In any case, if the
1006 preferred name can't be found, then one of two actions will be taken
1007 depending on the value of the variable `sy-use-only-preference-p'. If
1008 this is non-nil, then the `sy-default-attribution will be used. If it
1009 is nil, then a secondary scheme will be employed to find a suitable
1010 attribution scheme. First, the author's first name will be used. If
1011 that can't be found than the name list is searched for the first
1012 non-nil, non-empty name string. If still no name can be found, then
1013 the user is either queried, or the `sy-default-attribution' is used,
1014 depending on the value of `sy-confirm-always-p'.
1016 If the variable `sy-confirm-always-p' is non-nil, superyank will always
1017 confirm the attribution string with the user before inserting it into
1018 the reply buffer. Confirmation is with completion, but the completion
1019 list is merely a suggestion; the user can override the list by typing
1020 in a string of their choice.
1022 The variable `sy-rewrite-header-hook' is a hook that contains a lambda
1023 expression which rewrites the informative header at the top of the
1024 yanked message. Set to nil to avoid writing any header.
1026 You can make superyank autofill each paragraph it cites by setting the
1027 variable `sy-auto-fill-region-p' to non-nil. Or set the variable to nil
1028 and fill the paragraphs manually with sy-fill-paragraph-manually (see
1029 below).
1031 Finally, `sy-downcase-p' if non-nil, indicates that you always want to
1032 downcase the attribution string before insertion, and
1033 `sy-left-justify-p', if non-nil, indicates that you want to delete all
1034 leading white space before citing.
1036 Since the almost all yanking in other modes (RMAIL, GNUS) is done
1037 through the function `mail-yank-original', and since superyank
1038 overloads this function, cited yanking is automatically bound to the
1039 C-c C-y key. There are three other smaller functions that are
1040 provided with superyank and they are bound as below. Try C-h f on
1041 each function to get more information on these functions.
1043 Key Bindings:
1045 C-c C-y mail-yank-original (superyank's version)
1046 C-c q sy-fill-paragraph-manually
1047 C-c C-q sy-fill-paragraph-manually
1048 C-c i sy-insert-persist-attribution
1049 C-c C-i sy-insert-persist-attribution
1050 C-c C-o sy-open-line
1053 Summary of variables, with their default values:
1055 sy-default-attribution (default: \"Anon\")
1056 Attribution to use if no attribution string can be deciphered
1057 from the original message buffer.
1059 sy-citation-string (default: \">\")
1060 String to append to the attribution string for citation, for
1061 best results, it should be one character with no trailing space.
1063 sy-nested-citation-p (default: nil)
1064 Nil means use non-nested citations, non-nil means use old style
1065 nested citations.
1067 sy-cite-regexp (default: \"[a-zA-Z0-9]*>\")
1068 Regular expression that matches the beginning of a previously
1069 cited line. Always used at the beginning of a line so it does
1070 not need to start with a \"^\" character.
1072 sy-titlecue-regexp (default: \"\\s +-+\\s +\")
1073 Regular expression that matches a title delimiter in the name
1074 field.
1076 sy-preferred-attribution (default: 2)
1077 Integer indicating user's preferred attribution field.
1079 sy-confirm-always-p (default: t)
1080 Non-nil says always confirm with completion before inserting
1081 attribution.
1083 sy-rewrite-header-hook (default: 'sy-header-on-said)
1084 Hook for inserting informative header at the top of the yanked
1085 message.
1087 sy-downcase-p (default: nil)
1088 Non-nil says downcase the attribution string before insertion.
1090 sy-left-justify-p (default: nil)
1091 Non-nil says delete leading white space before citing.
1093 sy-auto-fill-region-p (default: nil)
1094 Non-nil says don't auto fill the region. T says auto fill the
1095 paragraph.
1097 sy-use-only-preference-p (default: nil)
1098 If nil, use backup scheme when preferred attribution string
1099 can't be found. If non-nil and preferred attribution string
1100 can't be found, then use sy-default-attribution."
1102 (interactive "P")
1104 (local-set-key "\C-cq" 'sy-fill-paragraph-manually)
1105 (local-set-key "\C-c\C-q" 'sy-fill-paragraph-manually)
1106 (local-set-key "\C-c\i" 'sy-insert-persist-attribution)
1107 (local-set-key "\C-c\C-i" 'sy-insert-persist-attribution)
1108 (local-set-key "\C-c\C-o" 'sy-open-line)
1110 (sy-yank-original arg))
1114 ;; ----------------------------------------------------------------------
1116 ;; based on Bruce Israel's "fill-paragraph-properly", and modified from
1117 ;; code posted by David C. Lawrence. Modified to use the persistant
1118 ;; attribution if none could be found from the paragraph.
1120 (defun sy-fill-paragraph-manually (arg)
1121 "Fill paragraph containing or following point, automatically finding
1122 the sy-cite-regexp and using it as the prefix. If the sy-cite-regexp
1123 is not in the first line of the paragraph, it makes a guess at what
1124 the fill-prefix for the paragraph should be by looking at the first
1125 line and taking anything up to the first alphanumeric character.
1127 Prefix arg means justify both sides of paragraph as well.
1129 This function just does fill-paragraph if the fill-prefix is set. If
1130 what it deduces to be the paragraph prefix (based on the first line)
1131 does not precede each line in the region, then the persistant
1132 attribution is used. The persistant attribution is just the last
1133 attribution string used to cite lines."
1135 (interactive "P")
1136 (save-excursion
1137 (forward-paragraph)
1138 (or (bolp)
1139 (newline 1))
1141 (let ((end (point))
1143 (fill-prefix fill-prefix))
1144 (backward-paragraph)
1145 (if (looking-at "\n")
1146 (forward-char 1))
1147 (setq st (point))
1148 (if fill-prefix
1150 (untabify st end) ;; die, scurvy tabs!
1152 ;; untabify might have made the paragraph longer character-wise,
1153 ;; make sure end reflects the correct location of eop.
1155 (forward-paragraph)
1156 (setq end (point))
1157 (goto-char st)
1158 (if (looking-at sy-cite-regexp)
1159 (setq fill-prefix (concat
1160 (buffer-substring
1161 st (progn (re-search-forward sy-cite-regexp)
1162 (point)))
1163 " "))
1165 ;; this regexp is is convenient because paragraphs quoted by simple
1166 ;; indentation must still yield to us <evil laugh>
1168 (while (looking-at "[^a-zA-Z0-9]")
1169 (forward-char 1))
1170 (setq fill-prefix (buffer-substring st (point))))
1171 (next-line 1) (beginning-of-line)
1172 (while (and (< (point) end)
1173 (not (string-equal fill-prefix "")))
1175 ;; if what we decided was the fill-prefix does not precede all
1176 ;; of the lines in the paragraph, we probably goofed. In this
1177 ;; case set it to the persistant attribution.
1179 (if (looking-at (regexp-quote fill-prefix))
1181 (setq fill-prefix sy-persist-attribution))
1182 (next-line 1)
1183 (beginning-of-line)))
1184 (fill-region-as-paragraph st end arg))))
1187 ;; ----------------------------------------------------------------------
1189 ;; insert the persistant attribution at point
1191 (defun sy-insert-persist-attribution ()
1192 "Insert the persistant attribution at the beginning of the line that
1193 point is on. This string is the last attribution confirmed and used
1194 in the yanked reply buffer."
1195 (interactive)
1196 (save-excursion
1197 (beginning-of-line)
1198 (insert-string sy-persist-attribution)))
1202 ;; ----------------------------------------------------------------------
1204 ;; open a line putting the attribution at the beginning
1206 (defun sy-open-line (arg)
1207 "Insert a newline and leave point before it. Also inserts the
1208 persistant attribution at the beginning of the line. With arg,
1209 inserts that many newlines."
1210 (interactive "p")
1211 (save-excursion
1212 (let ((start (point)))
1213 (open-line arg)
1214 (goto-char start)
1215 (forward-line)
1216 (while (< 0 arg)
1217 (sy-insert-persist-attribution)
1218 (forward-line 1)
1219 (setq arg (- arg 1))))))
1221 (provide 'superyank)
1223 ;;; superyank.el ends here