diff options
| author | Richard M. Stallman | 1993-01-08 15:30:08 +0000 |
|---|---|---|
| committer | Richard M. Stallman | 1993-01-08 15:30:08 +0000 |
| commit | ac96728ddaeb952da40ebda93e7b82b5953601a5 (patch) | |
| tree | 2b0378530d39d621f0d7bcfd6900c266a3ab0afd | |
| parent | 2e9c66ab52c654a568bebeeca78e5ee7a6143608 (diff) | |
| download | emacs-ac96728ddaeb952da40ebda93e7b82b5953601a5.tar.gz emacs-ac96728ddaeb952da40ebda93e7b82b5953601a5.zip | |
Initial revision
| -rw-r--r-- | lisp/sc.el | 1563 |
1 files changed, 1563 insertions, 0 deletions
diff --git a/lisp/sc.el b/lisp/sc.el new file mode 100644 index 00000000000..30b1b5f64e5 --- /dev/null +++ b/lisp/sc.el | |||
| @@ -0,0 +1,1563 @@ | |||
| 1 | ;; -*- Mode: Emacs-Lisp -*- | ||
| 2 | ;; sc.el -- Version 2.3 (used to be supercite.el) | ||
| 3 | |||
| 4 | ;; ========== Introduction ========== | ||
| 5 | ;; Citation and attribution package for various GNU emacs news and | ||
| 6 | ;; electronic mail reading subsystems. This version of supercite will | ||
| 7 | ;; interface to VM 4.40+ and MH-E 3.7 (shipped w/ emacs 18.57) as is. | ||
| 8 | ;; It will also interface with GNUS 3.12+, RMAIL 18.55+, GNEWS, and | ||
| 9 | ;; probably most other news/mail subsystems by using the overloading | ||
| 10 | ;; functions provided in sc-oloads.el (see that file or the README for | ||
| 11 | ;; more information on interfacing supercite with your reader subsystem). | ||
| 12 | ;; This version should now be compatible with Lucid Emacs 19.x emacses. | ||
| 13 | |||
| 14 | ;; This package does not do any yanking of messages, but instead | ||
| 15 | ;; massages raw reply buffers set up by the reply/forward functions in | ||
| 16 | ;; the news/mail subsystems. Therefore, such useful operations as | ||
| 17 | ;; yanking and citing portions of the original article (instead of the | ||
| 18 | ;; whole article) are not within the ability or responsiblity of | ||
| 19 | ;; supercite. | ||
| 20 | |||
| 21 | ;; ========== Disclaimer ========== | ||
| 22 | ;; This software is distributed in the hope that it will be useful, | ||
| 23 | ;; but WITHOUT ANY WARRANTY. No author or distributor, nor any | ||
| 24 | ;; author's past, present, or future employers accepts responsibility | ||
| 25 | ;; to anyone for the consequences of using it or for whether it serves | ||
| 26 | ;; any particular purpose or works at all, unless he says so in | ||
| 27 | ;; writing. | ||
| 28 | |||
| 29 | ;; Some of this software was written as part of the supercite author's | ||
| 30 | ;; official duty as an employee of the United States Government and is | ||
| 31 | ;; thus not subject to copyright. You are free to use that particular | ||
| 32 | ;; software as you wish, but WITHOUT ANY WARRANTY WHATSOEVER. It | ||
| 33 | ;; would be nice, though if when you use any of this or other freely | ||
| 34 | ;; available code, you give due credit to the author. | ||
| 35 | |||
| 36 | ;; Other parts of this code were written by other people. Wherever | ||
| 37 | ;; possible, credit to that author, and the copy* notice supplied by | ||
| 38 | ;; the author are included with that code. The supercite author is no | ||
| 39 | ;; longer an employee of the U.S. Government so the GNU Public Licence | ||
| 40 | ;; should be considered in effect for all enhancements and bug fixes | ||
| 41 | ;; performed by the author. | ||
| 42 | |||
| 43 | ;; ========== Author (unless otherwise stated) ======================== | ||
| 44 | ;; NAME: Barry A. Warsaw USMAIL: Century Computing, Inc. | ||
| 45 | ;; TELE: (301) 593-3330 1014 West Street | ||
| 46 | ;; INET: bwarsaw@cen.com Laurel, Md 20707 | ||
| 47 | ;; UUCP: uunet!cen.com!bwarsaw | ||
| 48 | ;; | ||
| 49 | ;; Want to be on the Supercite mailing list? | ||
| 50 | ;; | ||
| 51 | ;; Send articles to: | ||
| 52 | ;; Internet: supercite@anthem.nlm.nih.gov | ||
| 53 | ;; UUCP: uunet!anthem.nlm.nih.gov!supercite | ||
| 54 | ;; | ||
| 55 | ;; Send administrivia (additions/deletions to list, etc) to: | ||
| 56 | ;; Internet: supercite-request@anthem.nlm.nih.gov | ||
| 57 | ;; UUCP: uunet!anthem.nlm.nih.gov!supercite-request | ||
| 58 | |||
| 59 | ;; ========== Credits and Thanks ========== | ||
| 60 | ;; This package was derived from the Superyank 1.11 package as posted | ||
| 61 | ;; to the net. Superyank 1.11 was inspired by code and ideas from | ||
| 62 | ;; Martin Neitzel and Ashwin Ram. Supercite version 2.3 has evolved | ||
| 63 | ;; through the comments and suggestions of the supercite mailing list | ||
| 64 | ;; which consists of many authors and users of the various mail and | ||
| 65 | ;; news reading subsystems. | ||
| 66 | |||
| 67 | ;; Many folks on the supercite mailing list have contributed their | ||
| 68 | ;; help in debugging, making suggestions and supplying support code or | ||
| 69 | ;; bug fixes for the previous versions of supercite. I want to thank | ||
| 70 | ;; everyone who helped, especially (in no particular order): | ||
| 71 | ;; | ||
| 72 | ;; Mark D. Baushke, Khalid Sattar, David Lawrence, Chris Davis, Kyle | ||
| 73 | ;; Jones, Kayvan Sylvan, Masanobu Umeda, Dan Jacobson, Piet van | ||
| 74 | ;; Oostrum, Hamish (H.I.) Macdonald, and Joe Wells. | ||
| 75 | ;; | ||
| 76 | ;; I don't mean to leave anyone out. All who have helped have been | ||
| 77 | ;; appreciated. | ||
| 78 | |||
| 79 | ;; ========== Getting Started ========== | ||
| 80 | ;; Here is a quick guide to getting started with supercite. The | ||
| 81 | ;; information contained here is mostly excerpted from the more | ||
| 82 | ;; detailed explanations given in the accompanying README file. | ||
| 83 | ;; Naturally, there are many customizations you can do to give your | ||
| 84 | ;; replies that personalized flair, but the instructions in this | ||
| 85 | ;; section should be sufficient for getting started. | ||
| 86 | |||
| 87 | ;; With this release of supercite overloading is the only supported | ||
| 88 | ;; way to get supercite hooked into your favorite news/mail reading | ||
| 89 | ;; subsystems. Overloading will be necessary for RMAIL, GNUS, GNEWS, | ||
| 90 | ;; RNEWS and PCMAIL. Overloading will not be needed for VM 4.37+ or | ||
| 91 | ;; MH-E 3.7+. MH-E comes with emacs 18.57 but if you have an earlier | ||
| 92 | ;; version of emacs, you should be able to ftp MH-E 3.7 separately. Or | ||
| 93 | ;; you can extract the MH-E overloading stuff from version 2.1's | ||
| 94 | ;; sc-oloads.el. | ||
| 95 | |||
| 96 | ;; First, to connect supercite to any mail/news reading subsystem, put | ||
| 97 | ;; this in your .emacs file: | ||
| 98 | ;; | ||
| 99 | ;; (setq mail-yank-hooks 'sc-cite-original) ; for all but MH-E | ||
| 100 | ;; (setq mh-yank-hooks 'sc-cite-original) ; for MH-E only | ||
| 101 | ;; | ||
| 102 | ;; If supercite is not pre-loaded into your emacs session, you should | ||
| 103 | ;; add the following autoload: | ||
| 104 | ;; | ||
| 105 | ;; (autoload 'sc-cite-original "sc" "Supercite 2.3" t) | ||
| 106 | ;; | ||
| 107 | ;; Then, if you need to overload, put the following in your .emacs file: | ||
| 108 | ;; | ||
| 109 | ;; (defun my-sc-overload-hook () | ||
| 110 | ;; (require 'sc-oloads) ; be sure this file is on your load-path | ||
| 111 | ;; (sc-overload-functions)) | ||
| 112 | ;; | ||
| 113 | ;; (setq news-reply-mode-hook 'my-sc-overload-hook) ; for RNEWS,GNUS,GNEWS | ||
| 114 | ;; (setq mail-setup-hook 'my-sc-overload-hook) ; for RMAIL, PCMAIL | ||
| 115 | ;; | ||
| 116 | ;; Finally, if you want to customize supercite, you should do it in a | ||
| 117 | ;; function called my-supercite-hook and: | ||
| 118 | ;; | ||
| 119 | ;; (setq sc-load-hook 'my-supercite-hook) | ||
| 120 | |||
| 121 | (require 'sc-alist) | ||
| 122 | |||
| 123 | |||
| 124 | ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv | ||
| 125 | ;; start of user defined variables | ||
| 126 | ;; vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv | ||
| 127 | |||
| 128 | (defvar sc-nested-citation-p nil | ||
| 129 | "*Controls whether to use nested or non-nested citation style. | ||
| 130 | Non-nil uses nested citations, nil uses non-nested citations. Type | ||
| 131 | \\[sc-describe] for more information.") | ||
| 132 | |||
| 133 | (defvar sc-citation-leader " " | ||
| 134 | "*String comprising first part of a citation.") | ||
| 135 | |||
| 136 | (defvar sc-citation-delimiter ">" | ||
| 137 | "*String comprising third part of a citation. | ||
| 138 | This string is used in both nested and non-nested citations.") | ||
| 139 | |||
| 140 | (defvar sc-citation-separator " " | ||
| 141 | "*String comprising fourth and last part of a citation.") | ||
| 142 | |||
| 143 | (defvar sc-default-author-name "Anonymous" | ||
| 144 | "*String used when author's name cannot be determined.") | ||
| 145 | |||
| 146 | (defvar sc-default-attribution "Anon" | ||
| 147 | "*String used when author's attribution cannot be determined.") | ||
| 148 | |||
| 149 | ;; Noriya KOBAYASHI (nk@ics.osaka-u.ac.jp) writes to the supercite | ||
| 150 | ;; mailing list: | ||
| 151 | ;; I use supercite in Nemacs-3.3.2. In order to handle citation using | ||
| 152 | ;; Kanji, [...set sc-cite-regexp to...] | ||
| 153 | ;; "\\s *\\([a-zA-Z0-9]\\|\\cc\\|\\cC\\|\\ch\\|\\cH\\|\\ck\\|\\cK\\)*\\s *>+" | ||
| 154 | ;; | ||
| 155 | (defvar sc-cite-regexp "\\s *[-a-zA-Z0-9_.]*>+\\s *" | ||
| 156 | "*Regular expression describing how a already cited line begins. | ||
| 157 | The regexp is only used at the beginning of a line, so it doesn't need | ||
| 158 | to start with a '^'.") | ||
| 159 | |||
| 160 | (defvar sc-titlecue-regexp "\\s +-+\\s +" | ||
| 161 | "*Regular expression describing the separator between names and titles. | ||
| 162 | Set to nil to treat entire field as a name.") | ||
| 163 | |||
| 164 | (defvar sc-spacify-name-chars '(?_ ?* ?+ ?=) | ||
| 165 | "*List of characters to convert to spaces if found in an author's name.") | ||
| 166 | |||
| 167 | (defvar sc-nicknames-alist | ||
| 168 | '(("Michael" "Mike") | ||
| 169 | ("Daniel" "Dan") | ||
| 170 | ("David" "Dave") | ||
| 171 | ("Jonathan" "John") | ||
| 172 | ("William" "Bill") | ||
| 173 | ("Elizabeth" "Beth") | ||
| 174 | ("Elizabeth" "Betsy") | ||
| 175 | ("Kathleen" "Kathy") | ||
| 176 | ("Smith" "Smitty")) | ||
| 177 | "*Association list of names and their common nicknames. | ||
| 178 | Entries are of the form (NAME NICKNAME), and NAMEs can have more than | ||
| 179 | one nickname. Nicknames will not be automatically used as an | ||
| 180 | attribution string, since I'm not sure this is really polite, but if a | ||
| 181 | name is glommed from the author name and presented in the attribution | ||
| 182 | string completion list, the matching nicknames will also be presented. | ||
| 183 | Set this variable to nil to defeat nickname expansions. Also note that | ||
| 184 | nicknames are not put in the supercite information alist.") | ||
| 185 | |||
| 186 | (defvar sc-confirm-always-p t | ||
| 187 | "*If non-nil, always confirm attribution string before citing text body.") | ||
| 188 | |||
| 189 | (defvar sc-preferred-attribution 'firstname | ||
| 190 | "*Specifies which part of the author's name becomes the attribution. | ||
| 191 | The value of this variable must be one of the following quoted symbols: | ||
| 192 | |||
| 193 | emailname -- email terminus name | ||
| 194 | initials -- initials of author | ||
| 195 | firstname -- first name of author | ||
| 196 | lastname -- last name of author | ||
| 197 | middlename1 -- first middle name of author | ||
| 198 | middlename2 -- second middle name of author | ||
| 199 | ... | ||
| 200 | |||
| 201 | Middle name indexes can be any positive integer greater than 0, though | ||
| 202 | it is unlikely that many authors will supply more than one middle | ||
| 203 | name, if that many.") | ||
| 204 | |||
| 205 | (defvar sc-use-only-preference-p nil | ||
| 206 | "*Controls what happens when the preferred attribution cannot be found. | ||
| 207 | If non-nil, then sc-default-attribution will be used. If nil, then | ||
| 208 | some secondary scheme will be employed to find a suitable attribution | ||
| 209 | string.") | ||
| 210 | |||
| 211 | (defvar sc-downcase-p nil | ||
| 212 | "*Non-nil means downcase the attribution and citation strings.") | ||
| 213 | |||
| 214 | (defvar sc-rewrite-header-list | ||
| 215 | '((sc-no-header) | ||
| 216 | (sc-header-on-said) | ||
| 217 | (sc-header-inarticle-writes) | ||
| 218 | (sc-header-regarding-adds) | ||
| 219 | (sc-header-attributed-writes) | ||
| 220 | (sc-header-verbose) | ||
| 221 | (sc-no-blank-line-or-header) | ||
| 222 | ) | ||
| 223 | "*List of reference header rewrite functions. | ||
| 224 | The variable sc-preferred-header-style controls which function in this | ||
| 225 | list is chosen for automatic reference header insertions. Electric | ||
| 226 | reference mode will cycle through this list of functions. For more | ||
| 227 | information, type \\[sc-describe].") | ||
| 228 | |||
| 229 | (defvar sc-preferred-header-style 1 | ||
| 230 | "*Index into sc-rewrite-header-list specifying preferred header style. | ||
| 231 | Index zero accesses the first function in the list.") | ||
| 232 | |||
| 233 | (defvar sc-electric-references-p t | ||
| 234 | "*Use electric references if non-nil.") | ||
| 235 | |||
| 236 | (defvar sc-electric-circular-p t | ||
| 237 | "*Treat electric references as circular if non-nil.") | ||
| 238 | |||
| 239 | (defvar sc-mail-fields-list | ||
| 240 | '("date" "message-id" "subject" "newsgroups" "references" | ||
| 241 | "from" "return-path" "path" "reply-to" "organization" | ||
| 242 | "reply" ) | ||
| 243 | "*List of mail header whose values will be saved by supercite. | ||
| 244 | These values can be used in header rewrite functions by accessing them | ||
| 245 | with the sc-field function. Mail headers in this list are case | ||
| 246 | insensitive and do not require a trailing colon.") | ||
| 247 | |||
| 248 | (defvar sc-mumble-string "" | ||
| 249 | "*Value returned by sc-field if chosen field cannot be found.") | ||
| 250 | |||
| 251 | (defvar sc-nuke-mail-headers-p t | ||
| 252 | "*Nuke or don't nuke mail headers. | ||
| 253 | If non-nil, nuke mail headers after gleaning useful information from | ||
| 254 | them.") | ||
| 255 | |||
| 256 | (defvar sc-reference-tag-string ">>>>> " | ||
| 257 | "*String used at the beginning of built-in reference headers.") | ||
| 258 | |||
| 259 | (defvar sc-fill-paragraph-hook 'sc-fill-paragraph | ||
| 260 | "*Hook for filling a paragraph. | ||
| 261 | This hook gets executed when you fill a paragraph either manually or | ||
| 262 | automagically. It expects point to be within the extent of the | ||
| 263 | paragraph that is going to be filled. This hook allows you to use a | ||
| 264 | different paragraph filling package than the one supplied with | ||
| 265 | supercite.") | ||
| 266 | |||
| 267 | (defvar sc-auto-fill-region-p nil | ||
| 268 | "*If non-nil, automatically fill each paragraph after it has been cited.") | ||
| 269 | |||
| 270 | (defvar sc-auto-fill-query-each-paragraph-p nil | ||
| 271 | "*If non-nil, query before filling each paragraph. | ||
| 272 | No querying and no filling will be performed if sc-auto-fill-region-p | ||
| 273 | is set to nil.") | ||
| 274 | |||
| 275 | (defvar sc-fixup-whitespace-p nil | ||
| 276 | "*If non-nil, delete all leading white space before citing.") | ||
| 277 | |||
| 278 | (defvar sc-all-but-cite-p nil | ||
| 279 | "*If non-nil, sc-cite-original does everything but cite the text. | ||
| 280 | This is useful for manually citing large messages, or portions of | ||
| 281 | large messages. When non-nil, sc-cite-original will still set up all | ||
| 282 | necessary variables and databases, but will skip the citing routine | ||
| 283 | which modify the reply buffer's text.") | ||
| 284 | |||
| 285 | (defvar sc-load-hook nil | ||
| 286 | "*User definable hook. | ||
| 287 | Runs after supercite is loaded. Set your customizations here.") | ||
| 288 | |||
| 289 | (defvar sc-pre-hook nil | ||
| 290 | "*User definable hook. | ||
| 291 | Runs before sc-cite-original executes.") | ||
| 292 | |||
| 293 | (defvar sc-post-hook nil | ||
| 294 | "*User definable hook. | ||
| 295 | Runs after sc-cite-original executes.") | ||
| 296 | |||
| 297 | (defvar sc-header-nuke-list | ||
| 298 | '("via" "origin" "status" "received" "remailed" "cc" "sender" "replied" | ||
| 299 | "organization" "keywords" "distribution" "xref" "references" "expires" | ||
| 300 | "approved" "summary" "precedence" "subject" "newsgroup[s]?" | ||
| 301 | "\\(followup\\|apparently\\|errors\\|\\(\\(in-\\)?reply\\)?-\\)?to" | ||
| 302 | "x-[a-z0-9-]+" "[a-z-]*message-id" "\\(summary-\\)?line[s]" | ||
| 303 | "\\(\\(return\\|reply\\)-\\)?path" "\\(posted-\\)?date" | ||
| 304 | "\\(mail-\\)?from") | ||
| 305 | "*List of mail headers to remove from body of reply.") | ||
| 306 | |||
| 307 | |||
| 308 | |||
| 309 | ;; ====================================================================== | ||
| 310 | ;; keymaps | ||
| 311 | |||
| 312 | (defvar sc-default-keymap | ||
| 313 | '(lambda () | ||
| 314 | (local-set-key "\C-c\C-r" 'sc-insert-reference) | ||
| 315 | (local-set-key "\C-c\C-t" 'sc-cite) | ||
| 316 | (local-set-key "\C-c\C-a" 'sc-recite) | ||
| 317 | (local-set-key "\C-c\C-u" 'sc-uncite) | ||
| 318 | (local-set-key "\C-c\C-i" 'sc-insert-citation) | ||
| 319 | (local-set-key "\C-c\C-o" 'sc-open-line) | ||
| 320 | (local-set-key "\C-c\C-q" 'sc-fill-paragraph-manually) | ||
| 321 | (local-set-key "\C-cq" 'sc-fill-paragraph-manually) | ||
| 322 | (local-set-key "\C-c\C-m" 'sc-modify-information) | ||
| 323 | (local-set-key "\C-cf" 'sc-view-field) | ||
| 324 | (local-set-key "\C-cg" 'sc-glom-headers) | ||
| 325 | (local-set-key "\C-c\C-v" 'sc-version) | ||
| 326 | (local-set-key "\C-c?" 'sc-describe) | ||
| 327 | ) | ||
| 328 | "*Default keymap if major-mode can't be found in `sc-local-keymaps'.") | ||
| 329 | |||
| 330 | (defvar sc-local-keymaps | ||
| 331 | '((mail-mode | ||
| 332 | (lambda () | ||
| 333 | (local-set-key "\C-c\C-r" 'sc-insert-reference) | ||
| 334 | (local-set-key "\C-c\C-t" 'sc-cite) | ||
| 335 | (local-set-key "\C-c\C-a" 'sc-recite) | ||
| 336 | (local-set-key "\C-c\C-u" 'sc-uncite) | ||
| 337 | (local-set-key "\C-c\C-i" 'sc-insert-citation) | ||
| 338 | (local-set-key "\C-c\C-o" 'sc-open-line) | ||
| 339 | (local-set-key "\C-c\C-q" 'sc-fill-paragraph-manually) | ||
| 340 | (local-set-key "\C-cq" 'sc-fill-paragraph-manually) | ||
| 341 | (local-set-key "\C-c\C-m" 'sc-modify-information) | ||
| 342 | (local-set-key "\C-cf" 'sc-view-field) | ||
| 343 | (local-set-key "\C-cg" 'sc-glom-headers) | ||
| 344 | (local-set-key "\C-c\C-v" 'sc-version) | ||
| 345 | (local-set-key "\C-c?" 'sc-describe) | ||
| 346 | )) | ||
| 347 | (mh-letter-mode | ||
| 348 | (lambda () | ||
| 349 | (local-set-key "\C-c\C-r" 'sc-insert-reference) | ||
| 350 | (local-set-key "\C-c\C-t" 'sc-cite) | ||
| 351 | (local-set-key "\C-c\C-a" 'sc-recite) | ||
| 352 | (local-set-key "\C-c\C-u" 'sc-uncite) | ||
| 353 | (local-set-key "\C-ci" 'sc-insert-citation) | ||
| 354 | (local-set-key "\C-c\C-o" 'sc-open-line) | ||
| 355 | (local-set-key "\C-cq" 'sc-fill-paragraph-manually) | ||
| 356 | (local-set-key "\C-c\C-m" 'sc-modify-information) | ||
| 357 | (local-set-key "\C-cf" 'sc-view-field) | ||
| 358 | (local-set-key "\C-cg" 'sc-glom-headers) | ||
| 359 | (local-set-key "\C-c\C-v" 'sc-version) | ||
| 360 | (local-set-key "\C-c?" 'sc-describe) | ||
| 361 | )) | ||
| 362 | (news-reply-mode mail-mode) | ||
| 363 | (vm-mail-mode mail-mode) | ||
| 364 | (e-reply-mode mail-mode) | ||
| 365 | (n-reply-mode mail-mode) | ||
| 366 | ) | ||
| 367 | "*List of keymaps to use with the associated major-mode.") | ||
| 368 | |||
| 369 | (defvar sc-electric-mode-map nil | ||
| 370 | "*Keymap for sc-electric-mode.") | ||
| 371 | |||
| 372 | (if sc-electric-mode-map | ||
| 373 | nil | ||
| 374 | (setq sc-electric-mode-map (make-sparse-keymap)) | ||
| 375 | (define-key sc-electric-mode-map "p" 'sc-eref-prev) | ||
| 376 | (define-key sc-electric-mode-map "n" 'sc-eref-next) | ||
| 377 | (define-key sc-electric-mode-map "s" 'sc-eref-setn) | ||
| 378 | (define-key sc-electric-mode-map "j" 'sc-eref-jump) | ||
| 379 | (define-key sc-electric-mode-map "x" 'sc-eref-abort) | ||
| 380 | (define-key sc-electric-mode-map "\r" 'sc-eref-exit) | ||
| 381 | (define-key sc-electric-mode-map "\n" 'sc-eref-exit) | ||
| 382 | (define-key sc-electric-mode-map "q" 'sc-eref-exit) | ||
| 383 | (define-key sc-electric-mode-map "g" 'sc-eref-goto) | ||
| 384 | ) | ||
| 385 | |||
| 386 | ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 387 | ;; end of user defined variables | ||
| 388 | ;; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| 389 | |||
| 390 | |||
| 391 | ;; ====================================================================== | ||
| 392 | ;; global variables, not user accessable | ||
| 393 | |||
| 394 | (defconst sc-version-number "2.3" | ||
| 395 | "Supercite's version number.") | ||
| 396 | |||
| 397 | ;; when rnewspost.el patch is installed (or function is overloaded) | ||
| 398 | ;; this should be nil since supercite now does this itself. | ||
| 399 | (setq news-reply-header-hook nil) | ||
| 400 | |||
| 401 | ;; autoload for sc-electric-mode | ||
| 402 | (autoload 'sc-electric-mode "sc-elec" | ||
| 403 | "Quasi-major mode for viewing supercite reference headers." nil) | ||
| 404 | |||
| 405 | ;; global alists (gals), misc variables. make new bytecompiler happy | ||
| 406 | (defvar sc-gal-information nil | ||
| 407 | "Internal global alist variable containing information.") | ||
| 408 | (defvar sc-gal-attributions nil | ||
| 409 | "Internal global alist variable containing attributions.") | ||
| 410 | (defvar sc-fill-arg nil | ||
| 411 | "Internal fill argument holder.") | ||
| 412 | (defvar sc-cite-context nil | ||
| 413 | "Internal citation context holder.") | ||
| 414 | (defvar sc-force-confirmation-p nil | ||
| 415 | "Internal variable.") | ||
| 416 | |||
| 417 | (make-variable-buffer-local 'sc-gal-attributions) | ||
| 418 | (make-variable-buffer-local 'sc-gal-information) | ||
| 419 | (make-variable-buffer-local 'sc-leached-keymap) | ||
| 420 | (make-variable-buffer-local 'sc-fill-arg) | ||
| 421 | (make-variable-buffer-local 'sc-cite-context) | ||
| 422 | |||
| 423 | (setq-default sc-gal-attributions nil) | ||
| 424 | (setq-default sc-gal-information nil) | ||
| 425 | (setq-default sc-leached-keymap (current-local-map)) | ||
| 426 | (setq-default sc-fill-arg nil) | ||
| 427 | (setq-default sc-cite-context nil) | ||
| 428 | |||
| 429 | |||
| 430 | |||
| 431 | ;; ====================================================================== | ||
| 432 | ;; miscellaneous support functions | ||
| 433 | |||
| 434 | (defun sc-mark () | ||
| 435 | "Mark compatibility between emacs v18 and v19." | ||
| 436 | (let ((zmacs-regions nil)) | ||
| 437 | (mark))) | ||
| 438 | |||
| 439 | (defun sc-update-gal (attribution) | ||
| 440 | "Update the information alist. | ||
| 441 | Add ATTRIBUTION and compose the nested and non-nested citation | ||
| 442 | strings." | ||
| 443 | (let ((attrib (if sc-downcase-p (downcase attribution) attribution))) | ||
| 444 | (aput 'sc-gal-information "sc-attribution" attrib) | ||
| 445 | (aput 'sc-gal-information "sc-nested-citation" | ||
| 446 | (concat attrib sc-citation-delimiter)) | ||
| 447 | (aput 'sc-gal-information "sc-citation" | ||
| 448 | (concat sc-citation-leader | ||
| 449 | attrib | ||
| 450 | sc-citation-delimiter | ||
| 451 | sc-citation-separator)))) | ||
| 452 | |||
| 453 | (defun sc-valid-index-p (index) | ||
| 454 | "Returns t if INDEX is a valid index into sc-rewrite-header-list." | ||
| 455 | (let ((last (1- (length sc-rewrite-header-list)))) | ||
| 456 | (and (natnump index) ;; a number, and greater than or equal to zero | ||
| 457 | (<= index last) ;; less than or equal to the last index | ||
| 458 | ))) | ||
| 459 | |||
| 460 | (defun sc-string-car (namestring) | ||
| 461 | "Return the string-equivalent \"car\" of NAMESTRING. | ||
| 462 | |||
| 463 | example: (sc-string-car \"John Xavier Doe\") | ||
| 464 | => \"John\"" | ||
| 465 | (substring namestring | ||
| 466 | (progn (string-match "\\s *" namestring) (match-end 0)) | ||
| 467 | (progn (string-match "\\s *\\S +" namestring) (match-end 0)))) | ||
| 468 | |||
| 469 | (defun sc-string-cdr (namestring) | ||
| 470 | "Return the string-equivalent \"cdr\" of NAMESTRING. | ||
| 471 | |||
| 472 | example: (sc-string-cdr \"John Xavier Doe\") | ||
| 473 | => \"Xavier Doe\"" | ||
| 474 | (substring namestring | ||
| 475 | (progn (string-match "\\s *\\S +\\s *" namestring) | ||
| 476 | (match-end 0)))) | ||
| 477 | |||
| 478 | (defun sc-linepos (&optional position col-p) | ||
| 479 | "Return the character position at various line positions. | ||
| 480 | Optional POSITION can be one of the following symbols: | ||
| 481 | bol == beginning of line | ||
| 482 | boi == beginning of indentation | ||
| 483 | eol == end of line [default] | ||
| 484 | |||
| 485 | Optional COL-P non-nil returns current-column instead of character position." | ||
| 486 | (let ((tpnt (point)) | ||
| 487 | rval) | ||
| 488 | (cond | ||
| 489 | ((eq position 'bol) (beginning-of-line)) | ||
| 490 | ((eq position 'boi) (back-to-indentation)) | ||
| 491 | (t (end-of-line))) | ||
| 492 | (setq rval (if col-p (current-column) (point))) | ||
| 493 | (goto-char tpnt) | ||
| 494 | rval)) | ||
| 495 | |||
| 496 | |||
| 497 | ;; ====================================================================== | ||
| 498 | ;; this section snarfs mail fields and places them in the info alist | ||
| 499 | |||
| 500 | (defun sc-build-header-zap-regexp () | ||
| 501 | "Return a regexp for sc-mail-yank-clear-headers." | ||
| 502 | (let ((headers sc-header-nuke-list) | ||
| 503 | (regexp nil)) | ||
| 504 | (while headers | ||
| 505 | (setq regexp (concat regexp | ||
| 506 | "^" (car headers) ":" | ||
| 507 | (if (cdr headers) "\\|" nil))) | ||
| 508 | (setq headers (cdr headers))) | ||
| 509 | regexp)) | ||
| 510 | |||
| 511 | (defun sc-mail-yank-clear-headers (start end) | ||
| 512 | "Nuke mail headers between START and END." | ||
| 513 | (if (and sc-nuke-mail-headers-p sc-header-nuke-list) | ||
| 514 | (let ((regexp (sc-build-header-zap-regexp))) | ||
| 515 | (save-excursion | ||
| 516 | (goto-char start) | ||
| 517 | (if (search-forward "\n\n" end t) | ||
| 518 | (save-restriction | ||
| 519 | (narrow-to-region start (point)) | ||
| 520 | (goto-char start) | ||
| 521 | (while (let ((case-fold-search t)) | ||
| 522 | (re-search-forward regexp nil t)) | ||
| 523 | (beginning-of-line) | ||
| 524 | (delete-region (point) | ||
| 525 | (progn (re-search-forward "\n[^ \t]") | ||
| 526 | (forward-char -1) | ||
| 527 | (point))) | ||
| 528 | ))) | ||
| 529 | )))) | ||
| 530 | |||
| 531 | (defun sc-mail-fetch-field (field) | ||
| 532 | "Return the value of the header field FIELD. | ||
| 533 | The buffer is expected to be narrowed to just the headers of the | ||
| 534 | message." | ||
| 535 | (save-excursion | ||
| 536 | (goto-char (point-min)) | ||
| 537 | (let ((case-fold-search t) | ||
| 538 | (name (concat "^" (regexp-quote field) "[ \t]*:[ \t]*"))) | ||
| 539 | (goto-char (point-min)) | ||
| 540 | (if (re-search-forward name nil t) | ||
| 541 | (let ((opoint (point))) | ||
| 542 | (while (progn (forward-line 1) | ||
| 543 | (looking-at "[ \t]"))) | ||
| 544 | (buffer-substring opoint (1- (point)))))))) | ||
| 545 | |||
| 546 | (defun sc-fetch-fields (start end) | ||
| 547 | "Fetch the mail fields in the region from START to END. | ||
| 548 | These fields can be accessed in header rewrite functions with sc-field." | ||
| 549 | (save-excursion | ||
| 550 | (save-restriction | ||
| 551 | (narrow-to-region start end) | ||
| 552 | (goto-char start) | ||
| 553 | (let ((fields sc-mail-fields-list)) | ||
| 554 | (while fields | ||
| 555 | (let ((value (sc-mail-fetch-field (car fields))) | ||
| 556 | (next (cdr fields))) | ||
| 557 | (and value | ||
| 558 | (aput 'sc-gal-information (car fields) value)) | ||
| 559 | (setq fields next))) | ||
| 560 | (if (sc-mail-fetch-field "from") | ||
| 561 | (aput 'sc-gal-information "from" (sc-mail-fetch-field "from"))))))) | ||
| 562 | |||
| 563 | (defun sc-field (field) | ||
| 564 | "Return the alist information associated with the FIELD. | ||
| 565 | If FIELD is not a valid key, return sc-mumble-string." | ||
| 566 | (or (aget sc-gal-information field) sc-mumble-string)) | ||
| 567 | |||
| 568 | |||
| 569 | ;; ====================================================================== | ||
| 570 | ;; built-in reference header rewrite functions | ||
| 571 | |||
| 572 | (defun sc-no-header () | ||
| 573 | "Does nothing. Use this instead of nil to get a blank header." | ||
| 574 | ()) | ||
| 575 | |||
| 576 | (defun sc-no-blank-line-or-header() | ||
| 577 | "Similar to sc-no-header except it removes the preceeding blank line." | ||
| 578 | (if (not (bobp)) | ||
| 579 | (if (and (eolp) | ||
| 580 | (progn (forward-line -1) | ||
| 581 | (or (looking-at mail-header-separator) | ||
| 582 | (and (eq major-mode 'mh-letter-mode) | ||
| 583 | (mh-in-header-p))))) | ||
| 584 | (progn (forward-line) | ||
| 585 | (let ((kill-lines-magic t)) (kill-line)))))) | ||
| 586 | |||
| 587 | (defun sc-header-on-said () | ||
| 588 | "\"On <date>, <from> said:\", unless 1. the \"from\" field cannot be | ||
| 589 | found, in which case nothing is inserted; or 2. the \"date\" field is | ||
| 590 | missing in which case only the from part is printed." | ||
| 591 | (let* ((sc-mumble-string "") | ||
| 592 | (whofrom (sc-field "from")) | ||
| 593 | (when (sc-field "date"))) | ||
| 594 | (if (not (string= whofrom "")) | ||
| 595 | (insert sc-reference-tag-string | ||
| 596 | (if (not (string= when "")) | ||
| 597 | (concat "On " when ", ") "") | ||
| 598 | whofrom " said:\n")))) | ||
| 599 | |||
| 600 | (defun sc-header-inarticle-writes () | ||
| 601 | "\"In article <message-id>, <from> writes:\" | ||
| 602 | Treats \"message-id\" and \"from\" fields similar to sc-header-on-said." | ||
| 603 | (let* ((sc-mumble-string "") | ||
| 604 | (whofrom (sc-field "from")) | ||
| 605 | (msgid (sc-field "message-id"))) | ||
| 606 | (if (not (string= whofrom "")) | ||
| 607 | (insert sc-reference-tag-string | ||
| 608 | (if (not (string= msgid "")) | ||
| 609 | (concat "In article " msgid ", ") "") | ||
| 610 | whofrom " writes:\n")))) | ||
| 611 | |||
| 612 | (defun sc-header-regarding-adds () | ||
| 613 | "\"Regarding <subject>; <from> adds:\" | ||
| 614 | Treats \"subject\" and \"from\" fields similar to sc-header-on-said." | ||
| 615 | (let* ((sc-mumble-string "") | ||
| 616 | (whofrom (sc-field "from")) | ||
| 617 | (subj (sc-field "subject"))) | ||
| 618 | (if (not (string= whofrom "")) | ||
| 619 | (insert sc-reference-tag-string | ||
| 620 | (if (not (string= subj "")) | ||
| 621 | (concat "Regarding " subj "; ") "") | ||
| 622 | whofrom " adds:\n")))) | ||
| 623 | |||
| 624 | (defun sc-header-attributed-writes () | ||
| 625 | "\"<sc-attribution>\" == <sc-author> <address> writes: | ||
| 626 | Treats these fields in a similar manner to sc-header-on-said." | ||
| 627 | (let* ((sc-mumble-string "") | ||
| 628 | (whofrom (sc-field "from")) | ||
| 629 | (reply (sc-field "sc-reply-address")) | ||
| 630 | (from (sc-field "sc-from-address")) | ||
| 631 | (attr (sc-field "sc-attribution")) | ||
| 632 | (auth (sc-field "sc-author"))) | ||
| 633 | (if (not (string= whofrom "")) | ||
| 634 | (insert sc-reference-tag-string | ||
| 635 | (if (not (string= attr "")) | ||
| 636 | (concat "\"" attr "\" == " ) "") | ||
| 637 | (if (not (string= auth "")) | ||
| 638 | (concat auth " ") "") | ||
| 639 | (if (not (string= reply "")) | ||
| 640 | (concat "<" reply ">") | ||
| 641 | (if (not (string= from "")) | ||
| 642 | (concat "<" from ">") "")) | ||
| 643 | " writes:\n")))) | ||
| 644 | |||
| 645 | (defun sc-header-verbose () | ||
| 646 | "Very verbose, some say gross." | ||
| 647 | (let* ((sc-mumble-string "") | ||
| 648 | (whofrom (sc-field "from")) | ||
| 649 | (reply (sc-field "sc-reply-address")) | ||
| 650 | (from (sc-field "sc-from-address")) | ||
| 651 | (author (sc-field "sc-author")) | ||
| 652 | (date (sc-field "date")) | ||
| 653 | (org (sc-field "organization")) | ||
| 654 | (msgid (sc-field "message-id")) | ||
| 655 | (ngrps (sc-field "newsgroups")) | ||
| 656 | (subj (sc-field "subject")) | ||
| 657 | (refs (sc-field "references")) | ||
| 658 | (cite (sc-field "sc-citation")) | ||
| 659 | (nl sc-reference-tag-string)) | ||
| 660 | (if (not (string= whofrom "")) | ||
| 661 | (insert (if (not (string= date "")) | ||
| 662 | (concat nl "On " date ",\n") "") | ||
| 663 | (concat nl (if (not (string= author "")) | ||
| 664 | author | ||
| 665 | whofrom) "\n") | ||
| 666 | (if (not (string= org "")) | ||
| 667 | (concat nl "from the organization of " org "\n") "") | ||
| 668 | (if (not (string= reply "")) | ||
| 669 | (concat nl "who can be reached at: " reply "\n") | ||
| 670 | (if (not (string= from "")) | ||
| 671 | (concat nl "who can be reached at: " from "\n") "")) | ||
| 672 | (if (not (string= cite "")) | ||
| 673 | (concat nl "(whose comments are cited below with \"" | ||
| 674 | cite "\"),\n") "") | ||
| 675 | (if (not (string= msgid "")) | ||
| 676 | (concat nl "had this to say in article " msgid "\n") "") | ||
| 677 | (if (not (string= ngrps "")) | ||
| 678 | (concat nl "in newsgroups " ngrps "\n") "") | ||
| 679 | (if (not (string= subj "")) | ||
| 680 | (concat nl "concerning the subject of " subj "\n") "") | ||
| 681 | (if (not (string= refs "")) | ||
| 682 | (concat nl "(see " refs " for more details)\n") "") | ||
| 683 | )))) | ||
| 684 | |||
| 685 | |||
| 686 | ;; ====================================================================== | ||
| 687 | ;; this section queries the user for necessary information | ||
| 688 | |||
| 689 | (defun sc-query (&optional default) | ||
| 690 | "Query for an attribution string with the optional DEFAULT choice. | ||
| 691 | Returns the string entered by the user, if non-empty and non-nil, or | ||
| 692 | DEFAULT otherwise. If DEFAULT is not supplied, sc-default-attribution | ||
| 693 | is used." | ||
| 694 | (if (not default) (setq default sc-default-attribution)) | ||
| 695 | (let* ((prompt (concat "Enter attribution string: (default " default ") ")) | ||
| 696 | (query (read-string prompt))) | ||
| 697 | (if (or (null query) | ||
| 698 | (string= query "")) | ||
| 699 | default | ||
| 700 | query))) | ||
| 701 | |||
| 702 | (defun sc-confirm () | ||
| 703 | "Confirm the preferred attribution with the user." | ||
| 704 | (if (or sc-confirm-always-p | ||
| 705 | sc-force-confirmation-p) | ||
| 706 | (aput 'sc-gal-attributions | ||
| 707 | (let* ((default (aheadsym sc-gal-attributions)) | ||
| 708 | chosen | ||
| 709 | (prompt (concat "Complete " | ||
| 710 | (cond | ||
| 711 | ((eq sc-cite-context 'citing) "cite") | ||
| 712 | ((eq sc-cite-context 'reciting) "recite") | ||
| 713 | (t "")) | ||
| 714 | " attribution string: (default " | ||
| 715 | default ") ")) | ||
| 716 | (minibuffer-local-completion-map | ||
| 717 | (copy-keymap minibuffer-local-completion-map))) | ||
| 718 | (define-key minibuffer-local-completion-map "\C-g" | ||
| 719 | '(lambda () (interactive) (beep) (throw 'select-abort nil))) | ||
| 720 | (setq chosen (completing-read prompt sc-gal-attributions)) | ||
| 721 | (if (or (not chosen) | ||
| 722 | (string= chosen "")) | ||
| 723 | default | ||
| 724 | chosen))))) | ||
| 725 | |||
| 726 | |||
| 727 | ;; ====================================================================== | ||
| 728 | ;; this section contains primitive functions used in the email address | ||
| 729 | ;; parsing schemes. they extract name fields from various parts of | ||
| 730 | ;; the "from:" field. | ||
| 731 | |||
| 732 | (defun sc-style1-addresses (from-string &optional delim) | ||
| 733 | "Extract the author's email terminus from email address FROM-STRING. | ||
| 734 | Match addresses of the style \"name%[stuff].\" when called with DELIM | ||
| 735 | of \"%\" and addresses of the style \"[stuff]name@[stuff]\" when | ||
| 736 | called with DELIM \"@\". If DELIM is nil or not provided, matches | ||
| 737 | addresses of the style \"name\"." | ||
| 738 | (and (string-match (concat "[a-zA-Z0-9_-]+" delim) from-string 0) | ||
| 739 | (substring from-string | ||
| 740 | (match-beginning 0) | ||
| 741 | (- (match-end 0) (if (null delim) 0 1))))) | ||
| 742 | |||
| 743 | (defun sc-style2-addresses (from-string) | ||
| 744 | "Extract the author's email terminus from email address FROM-STRING. | ||
| 745 | Match addresses of the style \"[stuff]![stuff]...!name[stuff].\"" | ||
| 746 | (let ((eos (length from-string)) | ||
| 747 | (mstart (string-match "![a-zA-Z0-9_-]+\\([^!a-zA-Z0-9_-]\\|$\\)" | ||
| 748 | from-string 0)) | ||
| 749 | (mend (match-end 0))) | ||
| 750 | (and mstart | ||
| 751 | (substring from-string (1+ mstart) (- mend (if (= mend eos) 0 1))) | ||
| 752 | ))) | ||
| 753 | |||
| 754 | (defun sc-get-address (from-string author) | ||
| 755 | "Get the full email address path from FROM-STRING. | ||
| 756 | AUTHOR is the author's name (which is removed from the address)." | ||
| 757 | (let ((eos (length from-string))) | ||
| 758 | (if (string-match (concat "\\(^\\|^\"\\)" author | ||
| 759 | "\\(\\s +\\|\"\\s +\\)") from-string 0) | ||
| 760 | (let ((addr (substring from-string (match-end 0) eos))) | ||
| 761 | (if (and (= (aref addr 0) ?<) | ||
| 762 | (= (aref addr (1- (length addr))) ?>)) | ||
| 763 | (substring addr 1 (1- (length addr))) | ||
| 764 | addr)) | ||
| 765 | (if (string-match "[a-zA-Z0-9!@%._-]+" from-string 0) | ||
| 766 | (substring from-string (match-beginning 0) (match-end 0)) | ||
| 767 | "") | ||
| 768 | ))) | ||
| 769 | |||
| 770 | (defun sc-get-emailname (from-string) | ||
| 771 | "Get the email terminus name from FROM-STRING." | ||
| 772 | (cond | ||
| 773 | ((sc-style1-addresses from-string "%")) | ||
| 774 | ((sc-style1-addresses from-string "@")) | ||
| 775 | ((sc-style2-addresses from-string)) | ||
| 776 | ((sc-style1-addresses from-string nil)) | ||
| 777 | (t (substring from-string 0 10)))) | ||
| 778 | |||
| 779 | |||
| 780 | ;; ====================================================================== | ||
| 781 | ;; this section contains functions that will extract a list of names | ||
| 782 | ;; from the name field string. | ||
| 783 | |||
| 784 | (defun sc-spacify-name-chars (name) | ||
| 785 | (let ((len (length name)) | ||
| 786 | (s 0)) | ||
| 787 | (while (< s len) | ||
| 788 | (if (memq (aref name s) sc-spacify-name-chars) | ||
| 789 | (aset name s 32)) | ||
| 790 | (setq s (1+ s))) | ||
| 791 | name)) | ||
| 792 | |||
| 793 | (defun sc-name-substring (string start end extend) | ||
| 794 | "Extract the specified substring of STRING from START to END. | ||
| 795 | EXTEND is the number of characters on each side to extend the | ||
| 796 | substring." | ||
| 797 | (and start | ||
| 798 | (let ((sos (+ start extend)) | ||
| 799 | (eos (- end extend))) | ||
| 800 | (substring string sos | ||
| 801 | (or (string-match sc-titlecue-regexp string sos) eos) | ||
| 802 | )))) | ||
| 803 | |||
| 804 | (defun sc-extract-namestring (from-string) | ||
| 805 | "Extract the name string from FROM-STRING. | ||
| 806 | This should be the author's full name minus an optional title." | ||
| 807 | (let ((pstart (string-match "(.*)" from-string 0)) | ||
| 808 | (pend (match-end 0)) | ||
| 809 | (qstart (string-match "\".*\"" from-string 0)) | ||
| 810 | (qend (match-end 0)) | ||
| 811 | (bstart (string-match "\\([.a-zA-Z0-9_-]+\\s *\\)+" from-string 0)) | ||
| 812 | (bend (match-end 0))) | ||
| 813 | (sc-spacify-name-chars | ||
| 814 | (cond | ||
| 815 | ((sc-name-substring from-string pstart pend 1)) | ||
| 816 | ((sc-name-substring from-string qstart qend 1)) | ||
| 817 | ((sc-name-substring from-string bstart bend 0)) | ||
| 818 | )))) | ||
| 819 | |||
| 820 | (defun sc-chop-namestring (namestring) | ||
| 821 | "Convert NAMESTRING to a list of names. | ||
| 822 | |||
| 823 | example: (sc-namestring-to-list \"John Xavier Doe\") | ||
| 824 | => (\"John\" \"Xavier\" \"Doe\")" | ||
| 825 | (if (not (string= namestring "")) | ||
| 826 | (append (list (sc-string-car namestring)) | ||
| 827 | (sc-chop-namestring (sc-string-cdr namestring))))) | ||
| 828 | |||
| 829 | (defun sc-strip-initials (namelist) | ||
| 830 | "Extract the author's initials from the NAMELIST." | ||
| 831 | (if (not namelist) | ||
| 832 | nil | ||
| 833 | (concat (if (string= (car namelist) "") | ||
| 834 | "" | ||
| 835 | (substring (car namelist) 0 1)) | ||
| 836 | (sc-strip-initials (cdr namelist))))) | ||
| 837 | |||
| 838 | |||
| 839 | ;; ====================================================================== | ||
| 840 | ;; this section handles selection of the attribution and citation strings | ||
| 841 | |||
| 842 | (defun sc-populate-alists (from-string) | ||
| 843 | "Put important and useful information in the alists using FROM-STRING. | ||
| 844 | Return the list of name symbols." | ||
| 845 | (let* ((namelist (sc-chop-namestring (sc-extract-namestring from-string))) | ||
| 846 | (revnames (reverse (cdr namelist))) | ||
| 847 | (midnames (reverse (cdr revnames))) | ||
| 848 | (firstname (car namelist)) | ||
| 849 | (midnames (reverse (cdr revnames))) | ||
| 850 | (lastname (car revnames)) | ||
| 851 | (initials (sc-strip-initials namelist)) | ||
| 852 | (emailname (sc-get-emailname from-string)) | ||
| 853 | (n 1) | ||
| 854 | (symlist (list 'emailname 'initials 'firstname 'lastname))) | ||
| 855 | |||
| 856 | ;; put basic information | ||
| 857 | (aput 'sc-gal-attributions 'firstname firstname) | ||
| 858 | (aput 'sc-gal-attributions 'lastname lastname) | ||
| 859 | (aput 'sc-gal-attributions 'emailname emailname) | ||
| 860 | (aput 'sc-gal-attributions 'initials initials) | ||
| 861 | |||
| 862 | (aput 'sc-gal-information "sc-firstname" firstname) | ||
| 863 | (aput 'sc-gal-information "sc-lastname" lastname) | ||
| 864 | (aput 'sc-gal-information "sc-emailname" emailname) | ||
| 865 | (aput 'sc-gal-information "sc-initials" initials) | ||
| 866 | |||
| 867 | ;; put middle names and build sc-author entry | ||
| 868 | (let ((author (concat firstname " "))) | ||
| 869 | (while midnames | ||
| 870 | (let ((name (car midnames)) | ||
| 871 | (next (cdr midnames)) | ||
| 872 | (symbol (intern (format "middlename%d" n))) | ||
| 873 | (string (format "sc-middlename-%d" n))) | ||
| 874 | ;; first put new middlename | ||
| 875 | (aput 'sc-gal-attributions symbol name) | ||
| 876 | (aput 'sc-gal-information string name) | ||
| 877 | (setq n (1+ n)) | ||
| 878 | (nconc symlist (list symbol)) | ||
| 879 | |||
| 880 | ;; now build author name | ||
| 881 | (setq author (concat author name " ")) | ||
| 882 | |||
| 883 | ;; incr loop | ||
| 884 | (setq midnames next) | ||
| 885 | )) | ||
| 886 | (setq author (concat author lastname)) | ||
| 887 | |||
| 888 | ;; put author name and email address | ||
| 889 | (aput 'sc-gal-information "sc-author" author) | ||
| 890 | (aput 'sc-gal-information "sc-from-address" | ||
| 891 | (sc-get-address from-string author)) | ||
| 892 | (aput 'sc-gal-information "sc-reply-address" | ||
| 893 | (sc-get-address (sc-field "reply-to") author)) | ||
| 894 | ) | ||
| 895 | ;; return value | ||
| 896 | symlist)) | ||
| 897 | |||
| 898 | (defun sc-sort-attribution-alist () | ||
| 899 | "Put preferred attribution at head of attributions alist." | ||
| 900 | (asort 'sc-gal-attributions sc-preferred-attribution) | ||
| 901 | |||
| 902 | ;; use backup scheme if preference is not legal | ||
| 903 | (if (or (null sc-preferred-attribution) | ||
| 904 | (anot-head-p sc-gal-attributions sc-preferred-attribution) | ||
| 905 | (let ((prefval (aget sc-gal-attributions | ||
| 906 | sc-preferred-attribution))) | ||
| 907 | (or (null prefval) | ||
| 908 | (string= prefval "")))) | ||
| 909 | ;; no legal attribution | ||
| 910 | (if sc-use-only-preference-p | ||
| 911 | (aput 'sc-gal-attributions 'sc-user-query | ||
| 912 | (sc-query sc-default-attribution)) | ||
| 913 | ;; else use secondary scheme | ||
| 914 | (asort 'sc-gal-attributions 'firstname)))) | ||
| 915 | |||
| 916 | (defun sc-build-attribution-alist (from-string) | ||
| 917 | "Extract attributions from FROM-STRING, applying preferences." | ||
| 918 | (let ((symlist (sc-populate-alists from-string)) | ||
| 919 | (headval (progn (sc-sort-attribution-alist) | ||
| 920 | (aget sc-gal-attributions | ||
| 921 | (aheadsym sc-gal-attributions) t)))) | ||
| 922 | |||
| 923 | ;; for each element in the symlist, remove the corresponding | ||
| 924 | ;; key-value pair in the alist, then insert just the value. | ||
| 925 | (while symlist | ||
| 926 | (let ((value (aget sc-gal-attributions (car symlist) t)) | ||
| 927 | (next (cdr symlist))) | ||
| 928 | (if (not (or (null value) | ||
| 929 | (string= value ""))) | ||
| 930 | (aput 'sc-gal-attributions value)) | ||
| 931 | (adelete 'sc-gal-attributions (car symlist)) | ||
| 932 | (setq symlist next))) | ||
| 933 | |||
| 934 | ;; add nicknames to the completion list | ||
| 935 | (let ((gal sc-gal-attributions)) | ||
| 936 | (while gal | ||
| 937 | (let ((nns sc-nicknames-alist) | ||
| 938 | (galname (car (car gal)))) | ||
| 939 | (while nns | ||
| 940 | (if (string= galname (car (car nns))) | ||
| 941 | (aput 'sc-gal-attributions (car (cdr (car nns))))) | ||
| 942 | (setq nns (cdr nns))) | ||
| 943 | (setq gal (cdr gal))))) | ||
| 944 | |||
| 945 | ;; now reinsert the head (preferred) attribution unless it is nil, | ||
| 946 | ;; this effectively just moves the head value to the front of the | ||
| 947 | ;; list. | ||
| 948 | (if headval | ||
| 949 | (aput 'sc-gal-attributions headval)) | ||
| 950 | |||
| 951 | ;; check to be sure alist is not nil | ||
| 952 | (if (null sc-gal-attributions) | ||
| 953 | (aput 'sc-gal-attributions sc-default-attribution)))) | ||
| 954 | |||
| 955 | (defun sc-select () | ||
| 956 | "Select an attribution and create a citation string." | ||
| 957 | (cond | ||
| 958 | (sc-nested-citation-p | ||
| 959 | (sc-update-gal "")) | ||
| 960 | ((null (aget sc-gal-information "from" t)) | ||
| 961 | (aput 'sc-gal-information "sc-author" sc-default-author-name) | ||
| 962 | (sc-update-gal (sc-query sc-default-attribution))) | ||
| 963 | ((null sc-gal-attributions) | ||
| 964 | (sc-build-attribution-alist (aget sc-gal-information "from" t)) | ||
| 965 | (sc-confirm) | ||
| 966 | (sc-update-gal (aheadsym sc-gal-attributions))) | ||
| 967 | (t | ||
| 968 | (sc-confirm) | ||
| 969 | (sc-update-gal (aheadsym sc-gal-attributions)))) | ||
| 970 | t) | ||
| 971 | |||
| 972 | |||
| 973 | ;; ====================================================================== | ||
| 974 | ;; region citing and unciting | ||
| 975 | |||
| 976 | (defun sc-cite-region (start end) | ||
| 977 | "Cite a region delineated by START and END." | ||
| 978 | (save-excursion | ||
| 979 | ;; set real end-of-region | ||
| 980 | (goto-char end) | ||
| 981 | (forward-line 1) | ||
| 982 | (set-mark (point)) | ||
| 983 | ;; goto real beginning-of-region | ||
| 984 | (goto-char start) | ||
| 985 | (beginning-of-line) | ||
| 986 | (let ((fstart (point)) | ||
| 987 | (fend (point))) | ||
| 988 | (while (< (point) (sc-mark)) | ||
| 989 | ;; remove leading whitespace if desired | ||
| 990 | (and sc-fixup-whitespace-p | ||
| 991 | (fixup-whitespace)) | ||
| 992 | ;; if end of line then perhaps autofill | ||
| 993 | (cond ((eolp) | ||
| 994 | (or (= fstart fend) | ||
| 995 | (not sc-auto-fill-region-p) | ||
| 996 | (and sc-auto-fill-query-each-paragraph-p | ||
| 997 | (not (y-or-n-p "Fill this paragraph? "))) | ||
| 998 | (save-excursion (set-mark fend) | ||
| 999 | (goto-char (/ (+ fstart fend 1) 2)) | ||
| 1000 | (run-hooks 'sc-fill-paragraph-hook))) | ||
| 1001 | (setq fstart (point) | ||
| 1002 | fend (point))) | ||
| 1003 | ;; not end of line so perhap cite it | ||
| 1004 | ((not (looking-at sc-cite-regexp)) | ||
| 1005 | (insert (aget sc-gal-information "sc-citation"))) | ||
| 1006 | (sc-nested-citation-p | ||
| 1007 | (insert (aget sc-gal-information "sc-nested-citation")))) | ||
| 1008 | (setq fend (point)) | ||
| 1009 | (forward-line 1)) | ||
| 1010 | (and sc-auto-fill-query-each-paragraph-p | ||
| 1011 | (message " ")) | ||
| 1012 | ))) | ||
| 1013 | |||
| 1014 | (defun sc-uncite-region (start end cite-regexp) | ||
| 1015 | "Uncite a previously cited region delineated by START and END. | ||
| 1016 | CITE-REGEXP describes how a cited line of texts starts. Unciting also | ||
| 1017 | auto-fills paragraph if sc-auto-fill-region-p is non-nil." | ||
| 1018 | (save-excursion | ||
| 1019 | (set-mark end) | ||
| 1020 | (goto-char start) | ||
| 1021 | (beginning-of-line) | ||
| 1022 | (let ((fstart (point)) | ||
| 1023 | (fend (point))) | ||
| 1024 | (while (< (point) (sc-mark)) | ||
| 1025 | ;; if end of line, then perhaps autofill | ||
| 1026 | (cond ((eolp) | ||
| 1027 | (or (= fstart fend) | ||
| 1028 | (not sc-auto-fill-region-p) | ||
| 1029 | (and sc-auto-fill-query-each-paragraph-p | ||
| 1030 | (not (y-or-n-p "Fill this paragraph? "))) | ||
| 1031 | (save-excursion (set-mark fend) | ||
| 1032 | (goto-char (/ (+ fstart fend 1) 2)) | ||
| 1033 | (run-hooks 'sc-fill-paragraph-hook))) | ||
| 1034 | (setq fstart (point) | ||
| 1035 | fend (point))) | ||
| 1036 | ;; not end of line so perhaps uncite it | ||
| 1037 | ((looking-at cite-regexp) | ||
| 1038 | (save-excursion | ||
| 1039 | (save-restriction | ||
| 1040 | (narrow-to-region (sc-linepos 'bol) (sc-linepos)) | ||
| 1041 | (beginning-of-line) | ||
| 1042 | (delete-region (point-min) | ||
| 1043 | (progn (re-search-forward cite-regexp | ||
| 1044 | (point-max) | ||
| 1045 | t) | ||
| 1046 | (match-end 0))))))) | ||
| 1047 | (setq fend (point)) | ||
| 1048 | (forward-line 1))))) | ||
| 1049 | |||
| 1050 | |||
| 1051 | ;; ====================================================================== | ||
| 1052 | ;; this section contains paragraph filling support | ||
| 1053 | |||
| 1054 | (defun sc-guess-fill-prefix (&optional literalp) | ||
| 1055 | "Guess the fill prefix used on the current line. | ||
| 1056 | Use various heuristics to find the fill prefix. Search begins on first | ||
| 1057 | non-blank line after point. | ||
| 1058 | |||
| 1059 | 1) If fill-prefix is already bound to the empty string, return | ||
| 1060 | nil. | ||
| 1061 | |||
| 1062 | 2) If fill-prefix is already bound, but not to the empty | ||
| 1063 | string, return the value of fill-prefix. | ||
| 1064 | |||
| 1065 | 3) If the current line starts with the last chosen citation | ||
| 1066 | string, then that string is returned. | ||
| 1067 | |||
| 1068 | 4) If the current line starts with a string matching the regular | ||
| 1069 | expression sc-cite-regexp, return the match. Note that if | ||
| 1070 | optional LITERALP is provided and non-nil, then the *string* | ||
| 1071 | that matches the regexp is return. Otherwise, if LITERALP is | ||
| 1072 | not provided or is nil, the *regexp* sc-cite-regexp is | ||
| 1073 | returned. | ||
| 1074 | |||
| 1075 | 5) If the current line starts with any number of characters, | ||
| 1076 | followed by the sc-citation-delimiter and then white space, | ||
| 1077 | that match is returned. See comment #4 above for handling of | ||
| 1078 | LITERALP. | ||
| 1079 | |||
| 1080 | 6) Nil is returned." | ||
| 1081 | (save-excursion | ||
| 1082 | ;; scan for first non-blank line in the region | ||
| 1083 | (beginning-of-line) | ||
| 1084 | (skip-chars-forward "\n\t ") | ||
| 1085 | (beginning-of-line) | ||
| 1086 | (let ((citation (aget sc-gal-information "sc-citation")) | ||
| 1087 | (generic-citation | ||
| 1088 | (concat "\\s *[^ \t\n" sc-citation-delimiter "]+>\\s +"))) | ||
| 1089 | (cond | ||
| 1090 | ((string= fill-prefix "") nil) ;; heuristic #1 | ||
| 1091 | (fill-prefix) ;; heuristic #2 | ||
| 1092 | ((looking-at (regexp-quote citation)) citation) ;; heuristic #3 | ||
| 1093 | ((looking-at sc-cite-regexp) ;; heuristic #4 | ||
| 1094 | (if literalp | ||
| 1095 | (buffer-substring | ||
| 1096 | (point) | ||
| 1097 | (progn (re-search-forward (concat sc-cite-regexp "\\s *") | ||
| 1098 | (point-max) nil) | ||
| 1099 | (point))) | ||
| 1100 | sc-cite-regexp)) | ||
| 1101 | ((looking-at generic-citation) ;; heuristic #5 | ||
| 1102 | (if literalp | ||
| 1103 | (buffer-substring | ||
| 1104 | (point) | ||
| 1105 | (progn (re-search-forward generic-citation) (point))) | ||
| 1106 | generic-citation)) | ||
| 1107 | (t nil))))) ;; heuristic #6 | ||
| 1108 | |||
| 1109 | (defun sc-consistant-cite-p (prefix) | ||
| 1110 | "Check current paragraph for consistant citation. | ||
| 1111 | Scans to paragraph delineated by (forward|backward)-paragraph to see | ||
| 1112 | if all lines start with PREFIX. Returns t if entire paragraph is | ||
| 1113 | consistantly cited, nil otherwise." | ||
| 1114 | (save-excursion | ||
| 1115 | (let ((end (progn (forward-paragraph) | ||
| 1116 | (beginning-of-line) | ||
| 1117 | (or (not (eolp)) | ||
| 1118 | (forward-char -1)) | ||
| 1119 | (point))) | ||
| 1120 | (start (progn (backward-paragraph) | ||
| 1121 | (beginning-of-line) | ||
| 1122 | (or (not (eolp)) | ||
| 1123 | (forward-char 1)) | ||
| 1124 | (point))) | ||
| 1125 | (badline t)) | ||
| 1126 | (goto-char start) | ||
| 1127 | (beginning-of-line) | ||
| 1128 | (while (and (< (point) end) | ||
| 1129 | badline) | ||
| 1130 | (setq badline (looking-at prefix)) | ||
| 1131 | (forward-line 1)) | ||
| 1132 | badline))) | ||
| 1133 | |||
| 1134 | (defun sc-fill-start (fill-prefix) | ||
| 1135 | "Find buffer position of start of region which begins with FILL-PREFIX. | ||
| 1136 | Restrict scan to current paragraph." | ||
| 1137 | (save-excursion | ||
| 1138 | (let ((badline nil) | ||
| 1139 | (top (save-excursion | ||
| 1140 | (backward-paragraph) | ||
| 1141 | (beginning-of-line) | ||
| 1142 | (or (not (eolp)) | ||
| 1143 | (forward-char 1)) | ||
| 1144 | (point)))) | ||
| 1145 | (while (and (not badline) | ||
| 1146 | (> (point) top)) | ||
| 1147 | (forward-line -1) | ||
| 1148 | (setq badline (not (looking-at fill-prefix))))) | ||
| 1149 | (forward-line 1) | ||
| 1150 | (point))) | ||
| 1151 | |||
| 1152 | (defun sc-fill-end (fill-prefix) | ||
| 1153 | "Find the buffer position of end of region which begins with FILL-PREFIX. | ||
| 1154 | Restrict scan to current paragraph." | ||
| 1155 | (save-excursion | ||
| 1156 | (let ((badline nil) | ||
| 1157 | (bot (save-excursion | ||
| 1158 | (forward-paragraph) | ||
| 1159 | (beginning-of-line) | ||
| 1160 | (or (not (eolp)) | ||
| 1161 | (forward-char -1)) | ||
| 1162 | (point)))) | ||
| 1163 | (while (and (not badline) | ||
| 1164 | (< (point) bot)) | ||
| 1165 | (beginning-of-line) | ||
| 1166 | (setq badline (not (looking-at fill-prefix))) | ||
| 1167 | (forward-line 1))) | ||
| 1168 | (forward-line -1) | ||
| 1169 | (point))) | ||
| 1170 | |||
| 1171 | (defun sc-fill-paragraph () | ||
| 1172 | "Supercite's paragraph fill function. | ||
| 1173 | Fill the paragraph containing or following point. Use | ||
| 1174 | sc-guess-fill-prefix to find the fill-prefix for the paragraph. | ||
| 1175 | |||
| 1176 | If the paragraph is inconsistantly cited (mixed fill-prefix), then the | ||
| 1177 | user is queried to restrict the the fill to only those lines around | ||
| 1178 | point which begin with the fill prefix. | ||
| 1179 | |||
| 1180 | The variable sc-fill-arg is passed to fill-paragraph and | ||
| 1181 | fill-region-as-paragraph which controls justification of the | ||
| 1182 | paragraph. sc-fill-arg is set by sc-fill-paragraph-manually." | ||
| 1183 | (save-excursion | ||
| 1184 | (let ((pnt (point)) | ||
| 1185 | (fill-prefix (sc-guess-fill-prefix t))) | ||
| 1186 | (cond | ||
| 1187 | ((not fill-prefix) | ||
| 1188 | (fill-paragraph sc-fill-arg)) | ||
| 1189 | ((sc-consistant-cite-p fill-prefix) | ||
| 1190 | (fill-paragraph sc-fill-arg)) | ||
| 1191 | ((y-or-n-p "Inconsistent citation found. Restrict? ") | ||
| 1192 | (message "") | ||
| 1193 | (fill-region-as-paragraph (progn (goto-char pnt) | ||
| 1194 | (sc-fill-start fill-prefix)) | ||
| 1195 | (progn (goto-char pnt) | ||
| 1196 | (sc-fill-end fill-prefix)) | ||
| 1197 | sc-fill-arg)) | ||
| 1198 | (t | ||
| 1199 | (message "") | ||
| 1200 | (progn | ||
| 1201 | (setq fill-prefix (aget sc-gal-information "sc-citation")) | ||
| 1202 | (fill-paragraph sc-fill-arg))))))) | ||
| 1203 | |||
| 1204 | |||
| 1205 | ;; ====================================================================== | ||
| 1206 | ;; the following functions are the top level, interactive commands that | ||
| 1207 | ;; can be bound to key strokes | ||
| 1208 | |||
| 1209 | (defun sc-insert-reference (arg) | ||
| 1210 | "Insert, at point, a reference header in the body of the reply. | ||
| 1211 | Numeric ARG indicates which header style from sc-rewrite-header-list | ||
| 1212 | to use when rewriting the header. No supplied ARG indicates use of | ||
| 1213 | sc-preferred-header-style. | ||
| 1214 | |||
| 1215 | With just \\[universal-argument], electric reference insert mode is | ||
| 1216 | entered, regardless of the value of sc-electric-references-p. See | ||
| 1217 | sc-electric-mode for more information." | ||
| 1218 | (interactive "P") | ||
| 1219 | (if (consp arg) | ||
| 1220 | (sc-electric-mode) | ||
| 1221 | (let ((pref (cond ((sc-valid-index-p arg) arg) | ||
| 1222 | ((sc-valid-index-p sc-preferred-header-style) | ||
| 1223 | sc-preferred-header-style) | ||
| 1224 | (t 0)))) | ||
| 1225 | (if sc-electric-references-p (sc-electric-mode pref) | ||
| 1226 | (condition-case err | ||
| 1227 | (eval (nth pref sc-rewrite-header-list)) | ||
| 1228 | (void-function | ||
| 1229 | (progn (message | ||
| 1230 | "Symbol's function definition is void: %s. (Header %d)." | ||
| 1231 | (symbol-name (car (cdr err))) | ||
| 1232 | pref) | ||
| 1233 | (beep))) | ||
| 1234 | (error | ||
| 1235 | (progn (message "Error evaluating rewrite header function %d." | ||
| 1236 | pref) | ||
| 1237 | (beep))) | ||
| 1238 | ))))) | ||
| 1239 | |||
| 1240 | (defun sc-cite (arg) | ||
| 1241 | "Cite the region of text between point and mark. | ||
| 1242 | Numeric ARG, if supplied, is passed unaltered to sc-insert-reference." | ||
| 1243 | (interactive "P") | ||
| 1244 | (if (not (sc-mark)) | ||
| 1245 | (error "Please designate a region to cite (i.e. set the mark).")) | ||
| 1246 | (catch 'select-abort | ||
| 1247 | (let ((sc-cite-context 'citing) | ||
| 1248 | (sc-force-confirmation-p (interactive-p))) | ||
| 1249 | (sc-select) | ||
| 1250 | (undo-boundary) | ||
| 1251 | (let ((xchange (if (> (sc-mark) (point)) nil | ||
| 1252 | (exchange-point-and-mark) | ||
| 1253 | t))) | ||
| 1254 | (sc-insert-reference arg) | ||
| 1255 | (sc-cite-region (point) (sc-mark)) | ||
| 1256 | ;; leave point on first cited line | ||
| 1257 | (while (and (< (point) (sc-mark)) | ||
| 1258 | (not (looking-at (aget sc-gal-information | ||
| 1259 | (if sc-nested-citation-p | ||
| 1260 | "sc-nested-citation" | ||
| 1261 | "sc-citation"))))) | ||
| 1262 | (forward-line 1)) | ||
| 1263 | (and xchange | ||
| 1264 | (exchange-point-and-mark)) | ||
| 1265 | )))) | ||
| 1266 | |||
| 1267 | (defun sc-uncite () | ||
| 1268 | "Uncite the region between point and mark." | ||
| 1269 | (interactive) | ||
| 1270 | (if (not (sc-mark)) | ||
| 1271 | (error "Please designate a region to uncite (i.e. set the mark).")) | ||
| 1272 | (undo-boundary) | ||
| 1273 | (let ((xchange (if (> (sc-mark) (point)) nil | ||
| 1274 | (exchange-point-and-mark) | ||
| 1275 | t)) | ||
| 1276 | (fp (or (sc-guess-fill-prefix) | ||
| 1277 | ""))) | ||
| 1278 | (sc-uncite-region (point) (sc-mark) fp) | ||
| 1279 | (and xchange | ||
| 1280 | (exchange-point-and-mark)))) | ||
| 1281 | |||
| 1282 | (defun sc-recite () | ||
| 1283 | "Recite the region by first unciting then citing the text." | ||
| 1284 | (interactive) | ||
| 1285 | (if (not (sc-mark)) | ||
| 1286 | (error "Please designate a region to recite (i.e. set the mark).")) | ||
| 1287 | (catch 'select-abort | ||
| 1288 | (let ((sc-cite-context 'reciting) | ||
| 1289 | (sc-force-confirmation-p t)) | ||
| 1290 | (sc-select) | ||
| 1291 | (undo-boundary) | ||
| 1292 | (let ((xchange (if (> (sc-mark) (point)) nil | ||
| 1293 | (exchange-point-and-mark) | ||
| 1294 | t)) | ||
| 1295 | (fp (or (sc-guess-fill-prefix) | ||
| 1296 | ""))) | ||
| 1297 | (sc-uncite-region (point) (sc-mark) fp) | ||
| 1298 | (sc-cite-region (point) (sc-mark)) | ||
| 1299 | (and xchange | ||
| 1300 | (exchange-point-and-mark)) | ||
| 1301 | )))) | ||
| 1302 | |||
| 1303 | (defun sc-insert-citation () | ||
| 1304 | "Insert citation string at beginning of current line." | ||
| 1305 | (interactive) | ||
| 1306 | (save-excursion | ||
| 1307 | (beginning-of-line) | ||
| 1308 | (insert (aget sc-gal-information "sc-citation")))) | ||
| 1309 | |||
| 1310 | (defun sc-open-line (arg) | ||
| 1311 | "Insert a newline and leave point before it. | ||
| 1312 | Also inserts the guessed prefix at the beginning of the new line. With | ||
| 1313 | numeric ARG, inserts that many new lines." | ||
| 1314 | (interactive "p") | ||
| 1315 | (save-excursion | ||
| 1316 | (let ((start (point)) | ||
| 1317 | (string (or (sc-guess-fill-prefix t) | ||
| 1318 | ""))) | ||
| 1319 | (open-line arg) | ||
| 1320 | (goto-char start) | ||
| 1321 | (forward-line 1) | ||
| 1322 | (while (< 0 arg) | ||
| 1323 | (insert string) | ||
| 1324 | (forward-line 1) | ||
| 1325 | (setq arg (- arg 1)))))) | ||
| 1326 | |||
| 1327 | (defun sc-fill-paragraph-manually (arg) | ||
| 1328 | "Fill current cited paragraph. | ||
| 1329 | Really just runs the hook sc-fill-paragraph-hook, however it does set | ||
| 1330 | the global variable sc-fill-arg to the value of ARG. This is | ||
| 1331 | currently the only way to pass an argument to a hookified function." | ||
| 1332 | (interactive "P") | ||
| 1333 | (setq sc-fill-arg arg) | ||
| 1334 | (run-hooks 'sc-fill-paragraph-hook)) | ||
| 1335 | |||
| 1336 | (defun sc-modify-information (arg) | ||
| 1337 | "Interactively modify information in the information alist. | ||
| 1338 | \\[universal-argument] if supplied, deletes the entry from the alist. | ||
| 1339 | You can add an entry by supplying a key instead of completing." | ||
| 1340 | (interactive "P") | ||
| 1341 | (let* ((delete-p (consp arg)) | ||
| 1342 | (action (if delete-p "delete" "modify")) | ||
| 1343 | (defaultkey (aheadsym sc-gal-information)) | ||
| 1344 | (prompt (concat "Select information key to " | ||
| 1345 | action ": (default " | ||
| 1346 | defaultkey ") ")) | ||
| 1347 | (key (completing-read prompt sc-gal-information)) | ||
| 1348 | ) | ||
| 1349 | (if (or (string= key "") | ||
| 1350 | (null key)) | ||
| 1351 | (setq key defaultkey)) | ||
| 1352 | (if delete-p (adelete 'sc-gal-information key) | ||
| 1353 | (let* ((oldval (aget sc-gal-information key t)) | ||
| 1354 | (prompt (concat "Enter new value for key \"" | ||
| 1355 | key "\" (default \"" oldval "\") ")) | ||
| 1356 | (newval (read-input prompt))) | ||
| 1357 | (if (or (string= newval "") | ||
| 1358 | (null newval)) | ||
| 1359 | nil | ||
| 1360 | (aput 'sc-gal-information key newval) | ||
| 1361 | ))))) | ||
| 1362 | |||
| 1363 | (defun sc-view-field (arg) | ||
| 1364 | "View field values in the information alist. | ||
| 1365 | This is essentially an interactive version of sc-field, and is similar | ||
| 1366 | to sc-modify-information, except that the field values can't be | ||
| 1367 | modified. With \\[universal-argument], if supplied, inserts the value | ||
| 1368 | into the current buffer as well." | ||
| 1369 | (interactive "P") | ||
| 1370 | (let* ((defaultkey (aheadsym sc-gal-information)) | ||
| 1371 | (prompt (concat "View information key: (default " | ||
| 1372 | defaultkey ") ")) | ||
| 1373 | (key (completing-read prompt sc-gal-information))) | ||
| 1374 | (if (or (string= key "") | ||
| 1375 | (null key)) | ||
| 1376 | (setq key defaultkey)) | ||
| 1377 | (let* ((val (aget sc-gal-information key t)) | ||
| 1378 | (pval (if val (concat "\"" val "\"") "nil"))) | ||
| 1379 | (message "value of key %s: %s" key pval) | ||
| 1380 | (if (and key (consp arg)) (insert val))))) | ||
| 1381 | |||
| 1382 | (defun sc-glom-headers () | ||
| 1383 | "Glom information from mail headers in region between point and mark. | ||
| 1384 | Any old information is lost, unless an error occurs." | ||
| 1385 | (interactive) | ||
| 1386 | (let ((attr (copy-sequence sc-gal-attributions)) | ||
| 1387 | (info (copy-sequence sc-gal-information))) | ||
| 1388 | (setq sc-gal-attributions nil | ||
| 1389 | sc-gal-information nil) | ||
| 1390 | (let ((start (region-beginning)) | ||
| 1391 | (end (region-end)) | ||
| 1392 | (sc-force-confirmation-p t) | ||
| 1393 | (sc-cite-context nil)) | ||
| 1394 | (sc-fetch-fields start end) | ||
| 1395 | (if (null sc-gal-information) | ||
| 1396 | (progn | ||
| 1397 | (message "No mail headers found! Restoring old information.") | ||
| 1398 | (setq sc-gal-attributions attr | ||
| 1399 | sc-gal-information info)) | ||
| 1400 | (sc-mail-yank-clear-headers start end) | ||
| 1401 | (if (not (catch 'select-abort | ||
| 1402 | (condition-case foo | ||
| 1403 | (sc-select) | ||
| 1404 | (quit (beep) (throw 'select-abort nil))) | ||
| 1405 | )) | ||
| 1406 | (setq sc-gal-attributions attr | ||
| 1407 | sc-gal-information info)) | ||
| 1408 | )))) | ||
| 1409 | |||
| 1410 | (defun sc-version (arg) | ||
| 1411 | "Show supercite version. | ||
| 1412 | Universal argument (\\[universal-argument]) ARG inserts version | ||
| 1413 | information in the current buffer instead of printing the message in | ||
| 1414 | the echo area." | ||
| 1415 | (interactive "P") | ||
| 1416 | (if (consp arg) | ||
| 1417 | (insert "Using Supercite version " sc-version-number) | ||
| 1418 | (message "Using Supercite version %s" sc-version-number))) | ||
| 1419 | |||
| 1420 | |||
| 1421 | ;; ====================================================================== | ||
| 1422 | ;; leach onto current mode | ||
| 1423 | |||
| 1424 | (defun sc-append-current-keymap () | ||
| 1425 | "Append some useful key bindings to the current local key map. | ||
| 1426 | This searches sc-local-keymap for the keymap to install based on the | ||
| 1427 | major-mode of the current buffer." | ||
| 1428 | (let ((hook (car (cdr (assq major-mode sc-local-keymaps))))) | ||
| 1429 | (cond | ||
| 1430 | ((not hook) | ||
| 1431 | (run-hooks 'sc-default-keymap)) | ||
| 1432 | ((not (listp hook)) | ||
| 1433 | (setq hook (car (cdr (assq hook sc-local-keymaps)))) | ||
| 1434 | (run-hooks 'hook)) | ||
| 1435 | (t | ||
| 1436 | (run-hooks 'hook)))) | ||
| 1437 | (setq sc-leached-keymap (current-local-map))) | ||
| 1438 | |||
| 1439 | (defun sc-snag-all-keybindings () | ||
| 1440 | "Snag all keybindings in major-mode's current keymap." | ||
| 1441 | (let* ((curkeymap (current-local-map)) | ||
| 1442 | (symregexp ".*sc-.*\n") | ||
| 1443 | (docstring (substitute-command-keys "\\{curkeymap}")) | ||
| 1444 | (start 0) | ||
| 1445 | (maxend (length docstring)) | ||
| 1446 | (spooge "")) | ||
| 1447 | (while (and (< start maxend) | ||
| 1448 | (string-match symregexp docstring start)) | ||
| 1449 | (setq spooge (concat spooge (substring docstring | ||
| 1450 | (match-beginning 0) | ||
| 1451 | (match-end 0)))) | ||
| 1452 | (setq start (match-end 0))) | ||
| 1453 | spooge)) | ||
| 1454 | |||
| 1455 | (defun sc-spoogify-docstring () | ||
| 1456 | "Modifies (makes into spooge) the docstring for the current major mode. | ||
| 1457 | This will leach the keybinding descriptions for supercite onto the end | ||
| 1458 | of the current major mode's docstring. If major mode is preloaded, | ||
| 1459 | this function will first make a copy of the list associated with the | ||
| 1460 | mode, then modify this copy." | ||
| 1461 | (let* ((symfunc (symbol-function major-mode)) | ||
| 1462 | (doc-cdr (and (listp symfunc) (nthcdr 2 symfunc))) | ||
| 1463 | (doc-str (documentation major-mode))) | ||
| 1464 | (cond | ||
| 1465 | ;; is a docstring even provided? | ||
| 1466 | ((not (stringp doc-str))) | ||
| 1467 | ;; have we already leached on? | ||
| 1468 | ((string-match "Supercite" doc-str)) | ||
| 1469 | ;; lets build the new doc string | ||
| 1470 | (t | ||
| 1471 | (let* ((described (sc-snag-all-keybindings)) | ||
| 1472 | (commonstr " | ||
| 1473 | |||
| 1474 | The major mode for this buffer has been modified to include the | ||
| 1475 | Supercite 2.3 package for handling attributions and citations of | ||
| 1476 | original messages in email replies. For more information on this | ||
| 1477 | package, type \"\\[sc-describe]\".") | ||
| 1478 | (newdoc-str | ||
| 1479 | (concat doc-str commonstr | ||
| 1480 | (if (not (string= described "")) | ||
| 1481 | (concat "\n\nThe following keys are bound " | ||
| 1482 | "to Supercite commands:\n\n" | ||
| 1483 | described))) | ||
| 1484 | )) | ||
| 1485 | (cond | ||
| 1486 | (doc-cdr | ||
| 1487 | (condition-case nil | ||
| 1488 | (setcar doc-cdr newdoc-str) | ||
| 1489 | (error | ||
| 1490 | ;; the major mode must be preloaded, make a copy first | ||
| 1491 | (setq symfunc (copy-sequence (symbol-function major-mode)) | ||
| 1492 | doc-cdr (nthcdr 2 symfunc)) | ||
| 1493 | (setcar doc-cdr newdoc-str) | ||
| 1494 | (fset major-mode symfunc)))) | ||
| 1495 | ;; lemacs 19 byte-code. | ||
| 1496 | ;; Set function to a new byte-code vector with the | ||
| 1497 | ;; new documentation in the documentation slot (element 4). | ||
| 1498 | ;; We can't use aset because aset won't allow you to modify | ||
| 1499 | ;; a byte-code vector. | ||
| 1500 | ;; Include element 5 if the vector has one. | ||
| 1501 | (t | ||
| 1502 | (fset major-mode | ||
| 1503 | (apply 'make-byte-code | ||
| 1504 | (aref symfunc 0) (aref symfunc 1) | ||
| 1505 | (aref symfunc 2) (aref symfunc 3) | ||
| 1506 | newdoc-str | ||
| 1507 | (if (> (length symfunc) 5) | ||
| 1508 | (list (aref symfunc 5))))) | ||
| 1509 | ))))))) | ||
| 1510 | |||
| 1511 | |||
| 1512 | ;; ====================================================================== | ||
| 1513 | ;; this section contains default hooks and hook support for execution | ||
| 1514 | |||
| 1515 | (defun sc-cite-original () | ||
| 1516 | "Hook version of sc-cite. | ||
| 1517 | This is callable from the various mail and news readers' reply | ||
| 1518 | function according to the agreed upon standard. See \\[sc-describe] | ||
| 1519 | for more details. Sc-cite-original does not do any yanking of the | ||
| 1520 | original message but it does require a few things: | ||
| 1521 | |||
| 1522 | 1) The reply buffer is the current buffer. | ||
| 1523 | |||
| 1524 | 2) The original message has been yanked and inserted into the | ||
| 1525 | reply buffer. | ||
| 1526 | |||
| 1527 | 3) Verbose mail headers from the original message have been | ||
| 1528 | inserted into the reply buffer directly before the text of the | ||
| 1529 | original message. | ||
| 1530 | |||
| 1531 | 4) Point is at the beginning of the verbose headers. | ||
| 1532 | |||
| 1533 | 5) Mark is at the end of the body of text to be cited." | ||
| 1534 | (run-hooks 'sc-pre-hook) | ||
| 1535 | (setq sc-gal-attributions nil) | ||
| 1536 | (setq sc-gal-information nil) | ||
| 1537 | (let ((start (region-beginning)) | ||
| 1538 | (end (region-end))) | ||
| 1539 | (sc-fetch-fields start end) | ||
| 1540 | (sc-mail-yank-clear-headers start end) | ||
| 1541 | (if (not sc-all-but-cite-p) | ||
| 1542 | (sc-cite sc-preferred-header-style)) | ||
| 1543 | (sc-append-current-keymap) | ||
| 1544 | (sc-spoogify-docstring) | ||
| 1545 | (run-hooks 'sc-post-hook))) | ||
| 1546 | |||
| 1547 | |||
| 1548 | ;; ====================================================================== | ||
| 1549 | ;; describe this package | ||
| 1550 | ;; | ||
| 1551 | (defun sc-describe () | ||
| 1552 | "Supercite version 2.3 is now described in a texinfo manual which | ||
| 1553 | makes the documenation available both for online perusal via emacs' | ||
| 1554 | info system, or for hard-copy printing using the TeX facility. | ||
| 1555 | |||
| 1556 | To view the online document hit \\[info], then \"mSupercite <RET>\"." | ||
| 1557 | (interactive) | ||
| 1558 | (describe-function 'sc-describe)) | ||
| 1559 | |||
| 1560 | ;; ====================================================================== | ||
| 1561 | ;; load hook | ||
| 1562 | (run-hooks 'sc-load-hook) | ||
| 1563 | (provide 'sc) | ||