home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #16 / NN_1992_16.iso / spool / gnu / emacs / sources / 547 < prev    next >
Encoding:
Text File  |  1992-07-23  |  51.4 KB  |  1,477 lines

  1. Path: sparky!uunet!zaphod.mps.ohio-state.edu!uwm.edu!ogicse!mintaka.lcs.mit.edu!mintaka!mernst
  2. From: mernst@theory.lcs.mit.edu (Michael Ernst)
  3. Newsgroups: gnu.emacs.sources
  4. Subject: Love22 mode -- highlight certain words in text
  5. Message-ID: <MERNST.92Jul23092950@coot.lcs.mit.edu>
  6. Date: 23 Jul 92 14:29:50 GMT
  7. Article-I.D.: coot.MERNST.92Jul23092950
  8. Sender: news@mintaka.lcs.mit.edu
  9. Distribution: gnu
  10. Organization: MIT Lab for Computer Science
  11. Lines: 1464
  12.  
  13. I have recently received a number of requests for this old package, so I
  14. suppose it's time to post it to the net.  (I realize that there are better
  15. ways to rebind every key than the one I use here, but this works for me,
  16. and has for years.)
  17.  
  18. Enjoy!
  19.                     -Michael Ernst
  20.                      mernst@theory.lcs.mit.edu
  21.  
  22. ;;; -*- Mode:  Emacs-Lisp -*-
  23.  
  24. ;;; LOVE22.EL
  25. ;;; Michael D. Ernst <mernst@theory.lcs.mit.edu>
  26. ;;; 10/2/90
  27. ;;; last modified 10/18/90
  28.  
  29. ;; LCD Archive Entry:
  30. ;; love22|Michael D. Ernst|mernst@theory.lcs.mit.edu
  31. ;; |Support for writing and recognizing text in the style of Love 22.
  32. ;; |90-10-18|1.0|
  33.  
  34.  
  35. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  36. ;;; Description of love22.el
  37. ;;;
  38.  
  39. ;; This file helps you write text in the style of Love 22, a peripatetic
  40. ;; jester and perennial presidential candidate (on a platform of "FOOD",
  41. ;; "CLOTH", and "RENTS") who looks exactly like Uncle Sam, and dresses the
  42. ;; part.  He notices words such as "LOVE22" whose letters add up to 22 on
  43. ;; the ABC chart.
  44. ;;
  45. ;;    The ABC Chart
  46. ;;   A B C D E F G H I
  47. ;;   J K L M N O P Q R
  48. ;;   S T U V W X Y Z
  49. ;;   1 2 3 4 5 6 7 8 9
  50. ;;
  51. ;; In conversations, he points them out in real time, whether he's talking
  52. ;; or listening, and in writing he emphasizes them with capitalization and
  53. ;; quotation marks, sometimes substituting homonyms to make the addition
  54. ;; work out.  This program lets you do the same, automatically.  The effect
  55. ;; can be quite humorous in text -- some people mistake it for
  56. ;; Zippification.  Try it on some text you have lying around to see the
  57. ;; result.  (I have my mail-before-send-hook do this automatically on
  58. ;; certain mail, depending on the recipient.)
  59. ;;
  60. ;; You can get $22 bills, a copy of Love 22's platform, and other fun stuff
  61. ;; by sending a self-addressed stamped envelope (with a small donation, if
  62. ;; you're feeling generous; I sent $2.22 last time) to
  63. ;;    Love 22
  64. ;;    PO Box 4022
  65. ;;    Key West, FL  33040
  66.  
  67. ;; The commands of interest are:
  68. ;; 
  69. ;; show-abc-chart
  70. ;;    Displays the ABC chart in the *Help* buffer.
  71. ;; abc-chart-word
  72. ;;    Reports the ABC chart value of the word at point.
  73. ;;    In Love22 mode, bound to C-x =.
  74. ;; abc-chart-region
  75. ;;    Reports the ABC chart value of the current region.
  76. ;;    In Love22 mode, bound to M-=.
  77. ;; love22-buffer
  78. ;;    Attempts to find words whose ABC chart values are 22 (or its multiples).
  79. ;;    Will group words together, substitute homonyms, look for roots of words,
  80. ;;    and try various other tricks.  The function 22-hook is called on each
  81. ;;    such word (or group of words) found; its default action is to upcase the
  82. ;;    word(s), surround it by quotes, and change spaces to hyphens if
  83. ;;    substitution (e.g. "2" for "too") occurred.
  84. ;; love22-region
  85. ;;    Like love22-buffer; acts on the current region (between point and mark).
  86. ;; love22-mode
  87. ;;    A minor mode to support writing of Love22 text.  Love22 mode causes the
  88. ;;    (almost) continual display of the ABC chart value value of the current
  89. ;;    word, as well as emphasizing words whose value is a multiple of 22, if 
  90. ;;    love22-emphasize is non-nil.  (This mode's emphasis functionality is
  91. ;;    considerably less than that of love22-buffer, but it operates in real
  92. ;;    time.)
  93.  
  94.  
  95. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  96. ;;; How to use this file
  97. ;;;
  98.  
  99. ;;; Save this file as love22.el somewhere on your load-path.
  100.  
  101. ;; *****
  102. ;; Don't byte-compile this file; it only works interpreted.
  103. ;; *****
  104.  
  105. ;;; Add to your .emacs file (without the leading semicolons):
  106. ;;;   (autoload 'show-abc-chart "love22"
  107. ;;;         "Show the ABC chart in the *Help* window." t)
  108. ;;;   (autoload 'abc-chart-word "love22"
  109. ;;;         "Compute and display the ABC chart value of the word at point." t)
  110. ;;;   (autoload 'abc-chart-region "love22"
  111. ;;;         "Compute and display the ABC chart value of the current region." t)
  112. ;;;   (autoload 'love22-buffer "love22"
  113. ;;;         "Process a buffer to look like Love22 wrote it." t)
  114. ;;;   (autoload 'love22-region "love22"
  115. ;;;         "Process a region to look like Love22 wrote it." t)
  116. ;;;   (autoload 'love22-mode "love22"
  117. ;;;         "Minor mode for writing Love22 text." t)
  118. ;;; If love22.el is not on your load-path, you may need to specify a full
  119. ;;; path for the filename instead of just "love22" (eg "~/emacs/love22", if
  120. ;;; "~/emacs/" isn't on your load-path but that's where love22.el is).
  121.  
  122. ;;; Now in future editing sessions you'll be ready to use the love22 
  123. ;;; commands (for instance, M-x love22-region).  To use them now, first do
  124. ;;;   M-x load-file RET love22.el
  125.  
  126. ;;; Changing values in the "Global variables" section modifies the behavior
  127. ;;; of the program; the experienced user may wish to play with these.
  128.  
  129. ;;; Yes, you could use this as a filter from the command line by running emacs
  130. ;;; in batch mode.  This exercise is left to the reader.
  131.  
  132. ;;; Enjoy!
  133.  
  134.  
  135. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  136. ;;; Contents
  137. ;;;
  138.  
  139. ;;; Description of love22.el
  140. ;;; How to use this file
  141. ;;; Contents
  142. ;;; To do
  143. ;;; Global variables
  144. ;;; Datatypes
  145. ;;; ABC Chart manipulation
  146. ;;; "Love22" a region or buffer
  147. ;;; Word roots
  148. ;;; 22-hook
  149. ;;; Love22 minor mode
  150. ;;; Love22-new-command
  151. ;;; Overlay and restore by modifying function cell binding
  152. ;;; Overlay and restore by replacing in keymap
  153. ;;; Overlay and restore -- top-level functions
  154. ;;; Utilities
  155.  
  156.  
  157. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  158. ;;; To do
  159. ;;;
  160.  
  161. ;;; Bug:  After turning love22 mode on and off, C-= is still bound to
  162. ;;;       abc-chart-word.
  163.  
  164. ;;; Make this usable in batch mode, and describe how to do it.
  165.  
  166. ;;; Figure out why this won't byte-compile, fix it, add the following
  167. ;;; to the instructions.
  168. ;; ;;; Type 
  169. ;; ;;;   M-x byte-compile-file RET love22.el
  170. ;; ;;; to byte-compile the file (it's too slow when it runs interpreted).
  171. ;; ;;; (M-x = Meta-x (hold down meta, press x), RET = the return key)
  172.  
  173. ;;; Perhaps interactively ask each time that a love22 word is about to be
  174. ;;; 22-hook'ed.  (Only problem is that the current solution doesn't permit
  175. ;;; overlapping 22 words, since when something qualifies the rest of the
  176. ;;; history is thrown away.  This shouldn't be overly difficult to fix.)
  177.  
  178. ;;; Instead of building lists front-to-back, I ought to build them in
  179. ;;; reverse order, then reverse them later.  nconc costs time to walk down
  180. ;;; the list to the end...
  181.  
  182. ;;; Make the tex stuff settable, so I could get, say, bold instead of
  183. ;;; quotation with uppercasing.
  184.  
  185. ;;; Make suffix-checking more efficient.
  186. ;;;  * rewrite suffixp better
  187. ;;;  * reverse the word and then compare from the front in word-root
  188.  
  189. (provide 'love22)
  190.  
  191.  
  192. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  193. ;;; Global variables
  194. ;;;
  195.  
  196. ;;; These variables are grouped into behavior modifiers, root exceptions, 
  197. ;;; conversion list, and syntax table.  The asterisk at the beginning of 
  198. ;;; the documentation string means that the user is permitted to change 
  199. ;;; the variable.
  200.  
  201.  
  202. ;;; BEHAVIOR MODIFIERS
  203.  
  204. (defvar love22-emphasize t
  205.   "*If non-nil, then in Love22 mode 22-words are automatically emphasized.")
  206.  
  207. (defvar love22-explicit-sum nil
  208.   "*If non-nil, when a word with ABC chart value > 22 is emphasized, the actual
  209. value is noted afterward.  This is done by love22-buffer and love22-region,
  210. not by love22-mode, which never notes the value.")
  211.  
  212.  
  213. ;;; ROOT EXCEPTIONS
  214.  
  215. ;;; These variables enumerate exceptions to the usual rules for finding
  216. ;;; roots of words.  The strings should be lowercase.
  217.  
  218. ;; Consider adding "est" to the list of suffixes.  It might be too
  219. ;; complicated, too many exceptions, though.
  220.  
  221. (defvar love22-ly-exceptions 
  222.   '("belly" "only" "really")
  223.   "*Words whose roots can't be found by removing the \"ly\".")
  224.  
  225. (defvar love22-nt-exceptions 
  226.   '("ain't" "can't" "don't" "shan't" "won't")
  227.   "*Words whose roots can't be found by removing the \"n't\".
  228. I believe that this list is exhaustive.")
  229.  
  230. (defvar love22-es-exceptions 
  231.   '("james" "molasses" "rhodes" "ulysses" "yes")
  232.   "*Words ending in \"es\" whose singulars cannot be found by removing the \"es\" or \"s\".")
  233.  
  234. (defvar love22-es-plural-suffixes
  235.   '("ch" "sh" "th" "o" "ss" "x" "ay" "ey" "iy" "oy" "uy")
  236.   "*Words ending with one of these suffixes add \"es\" to make the plural.")
  237.  
  238. (defvar love22-es-s-exceptions
  239.   '("shoes" "horseshoes")
  240.   "*Words ending in "\es\" that look like "\es\" was added to make the plural,
  241. but actually only \"s\" was.  In other words, these words have some member of
  242. love22-es-plural-suffixes immediately before the \"es\".")
  243.  
  244. (defvar love22-s-exceptions 
  245.   '("as" "carlos" "has" "its" "towards" "was" "wumpus")
  246.   "*Words ending in \"s\" whose singulars can't be found by removing the \"s\".
  247. Words ending in \"es\" should appear in love22-es-exceptions, not here.")
  248.  
  249. (defvar love22-ous-exceptions
  250.   '("bayous" "bijous" "cachous" "carcajous" "caribous" "chous" "congous"
  251.     "froufrous" "ious" "kinkajous" "manitous" "marabous" "sous" "tinamous")
  252.   "*Words ending in \"ous\" which are plural nouns, not adjectives.
  253. I believe that this list is exhaustive.
  254. \"loups-garous\" is omitted since two s's were added to make the plural.")
  255.  
  256. (defvar love22-ism-exceptions 
  257.   '("schism")
  258.   "*Words whose roots can't be found by removing the \"ism\".")
  259.   
  260. (defvar love22-ist-exceptions
  261.   '("fist" "twist")
  262.   "*Words whose roots can't be found by removing the \"ist\".")
  263.  
  264.  
  265. ;;; CONVERSION LIST
  266.  
  267. ;;; These could go both directions, and possibly among homonyms (eg to => too)
  268. ;;; as well, but those seem less in the spirit of the thing.
  269. ;;; The inputs and results are lowercase for uniformity.
  270. ;;; None of the conversion inputs is a 22-word, else it shouldn't have been 
  271. ;;; here to begin with.  (This is not required, however.)
  272. ;;; I should make sure none of these has the same ABC chart value before and
  273. ;;; after, though the code works correctly (ie, uses the original value) 
  274. ;;; regardless.
  275. (defvar love22-conversion-alist
  276.   '(
  277.     ;; letter homonyms
  278.     ("be" . "B")
  279.     ("see" . "C")
  280.     ("sea" . "C")
  281.     ("eye" . "I")
  282.     ("oh" . "O")
  283.     ("cue" . "Q")
  284.     ("queue" . "Q")
  285.     ("are" . "R")
  286.     ("tee" . "T")
  287.     ("tea" . "T")
  288.     ("you" . "U")
  289.     ("why" . "Y")
  290.     ;; number homonyms
  291.     ("won" . "1")
  292.     ("to" . "2")
  293.     ("too" . "2")
  294.     ("for" . "4")
  295.     ("ate" . "8")
  296.     ("nein" . "9")
  297.     ;; numbers
  298.     ("one" . "1")
  299.     ("two" . "2")
  300.     ("three" . "3")
  301.     ("four" . "4")
  302.     ("five" . "5")
  303.     ("six" . "6")
  304.     ("seven" . "7")
  305.     ("eight" . "8")
  306.     ("nine" . "9")
  307.     ("ten" . "10")
  308.     ;; synonyms
  309.     ("can't" . "cannot")        ;; cannot is a 22-word
  310.     
  311.     ;; How to deal with multi-word substitutions?  These could be especially
  312.     ;; troublesome if only one of the two words is part (or all) of a 22-word.
  313.     ;; ("won't" . "will not")
  314.     ;; likewise with ("ain't" "can't" "don't" "shan't" "won't")
  315.     ;; Don't want to just treat "will not" as a single word because of 22-hook. 
  316.     
  317.     )
  318.   "*Alist of word conversions used to give phrases a 22 ABC chart value."
  319.   )
  320.  
  321.  
  322. ;;; SYNTAX TABLE
  323.  
  324. (defvar love22-syntax-table nil
  325.   "*Syntax table used by love22 to find word boundaries.")
  326.  
  327. ;; What if the syntax table changes?  What if I change buffers or modes?
  328. ;; What if I invoke this from a buffer with a weird syntax table?
  329. ;; Solution:  use the text mode syntax table as a base and hope for the best.
  330. (if (null love22-syntax-table)
  331.     (progn
  332.       (setq love22-syntax-table (copy-syntax-table text-mode-syntax-table))
  333.       ;; Make - into inter-word punctuation.
  334.       (modify-syntax-entry ?- "'" love22-syntax-table)
  335.       ;; Make ' a word constituent.
  336.       (modify-syntax-entry ?' "w" love22-syntax-table)))
  337.  
  338.  
  339. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  340. ;;; Datatypes
  341. ;;;
  342.  
  343. ;;; DESCRIPTION
  344. ;; A description represents a word unit (a word, a converted word, or the 
  345. ;; root of a word) via a vector of:
  346. ;;   ABC-value (a number)
  347. ;;   start (a point)
  348. ;;   length (nil if whole word, else number of characters in root)
  349. ;;   text (t if the text is what actually appears in the buffer)
  350.  
  351. (defmacro make-description (abc-value start length text)
  352.   (list 'vector abc-value start length text))
  353. (defmacro description-abc-value (desc)
  354.   (list 'aref desc 0))
  355. (defmacro description-start (desc)
  356.   (list 'aref desc 1))
  357. (defmacro description-length (desc)
  358.   (list 'aref desc 2))
  359. (defmacro description-text (desc)
  360.   (list 'aref desc 3))
  361. ;; For use by mapcar
  362. (defun description-text-fn (desc)
  363.   (description-text desc))
  364. ;; If the length is nil, it's no root.  If the length is a number, it is.
  365. (defmacro description-rootp (desc)
  366.   (list 'description-length desc))
  367.  
  368. ;;; TRACE
  369. ;; A trace is a view of the previous few words in the buffer.
  370. ;; Several traces may apply, if a recent word can be converted.
  371. ;; A trace is a list of descriptions, in reverse order (ie, most recent first).
  372.  
  373. (defvar love22-traces nil
  374.   "A list of traces, most preferred first.")
  375.  
  376.  
  377. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  378. ;;; ABC Chart manipulation
  379. ;;;
  380.  
  381. (defun show-abc-chart ()
  382.   "Display the ABC chart relating letters to their numeric values."
  383.   (interactive)
  384.   (with-output-to-temp-buffer "*Help*"
  385.     (princ " The ABC Chart\n\n")
  386.     (princ "A B C D E F G H I\n")
  387.     (princ "J K L M N O P Q R\n")
  388.     (princ "S T U V W X Y Z  \n")
  389.     (princ "1 2 3 4 5 6 7 8 9\n")
  390.     (print-help-return-message)))
  391.  
  392. ;; perhaps do this more elegantly.
  393. (defvar abc-chart 
  394.   (let ((chart (make-vector 256 0)))
  395.     (setq ch ?1)
  396.     (setq val 1)
  397.     (while (<= ch ?9)
  398.       (aset chart ch val)
  399.       (setq val (1+ val))
  400.       (setq ch (1+ ch)))
  401.     (setq ch ?a)
  402.     (setq val 1)
  403.     (while (<= ch ?z)
  404.       (aset chart ch val)
  405.       (aset chart (upcase ch) val)
  406.       (setq val (if (= val 9) 1 (1+ val)))
  407.       (setq ch (1+ ch)))
  408.     chart)
  409.   "The ABC chart assigning numeric values to characters.")
  410.  
  411. (defmacro abc-chart-char (char)
  412.   (` (aref abc-chart (, char))))
  413. ;; For use by mapcar
  414. (defun abc-chart-char-fn (char)
  415.   (abc-chart-char char))
  416.  
  417. ;; This does NOT do clever substitutions.
  418. (defun abc-chart-string (string)
  419.   (apply '+ (mapcar 'abc-chart-char-fn string)))
  420.  
  421. ;; This is adapted from ispell.el.
  422. (defun love22-current-word (&optional noerr)
  423.   "Returns a cons of (start . end) for the current word.
  424. Errs if there is no current word, or returns nil if NOERR is true."
  425.   (with-love22-syntax-table
  426.    (save-excursion
  427.      (if (not (looking-at "\\w"))
  428.      ;; Move backward for word if not already on one
  429.      (re-search-backward "\\w" (point-min) 'stay))
  430.      ;; Move to start of word
  431.      (re-search-backward "\\W" (point-min) 'stay)
  432.      ;; Find start and end of word
  433.      (if (re-search-forward "\\w+" nil t)
  434.      (cons (match-beginning 0) (match-end 0))
  435.        (if (not noerr)
  436.        (error "No word to check."))))))
  437.  
  438. (defun abc-chart-word (&optional noerr terse)
  439.   "Check word at or before point on the ABC chart.
  440. If NOERR is non-nil, then doesn't err if there is no current word.
  441. If TERSE is non-nil, the output is abbreviated."
  442.   (interactive)
  443.   (let ((region (love22-current-word noerr)))
  444.     (if region
  445.     (abc-chart-region (car region) (cdr region) terse))))
  446.  
  447. (defun abc-chart-region (start end &optional terse)
  448.   "Print the sum of the ABC chart values of the characters in the region.
  449. If TERSE is non-nil, the output is abbreviated."
  450.   (interactive "r")
  451.   (let ((region-string (buffer-substring start end)))
  452.     (message (if terse
  453.          "%s = %d"
  454.            "%s = %d on the ABC chart.")
  455.          region-string (abc-chart-string region-string))))
  456.  
  457.  
  458.  
  459. (defun abc-chart-word-emphasize-maybe (&optional noerr terse)
  460.   "Check word at or before point on the ABC chart.  If = 22, then emphasize it.
  461. This needs to be interactive because it gets bound to keys."
  462.   (interactive)
  463.   (let ((region (love22-current-word noerr)))
  464.     (if region
  465.     (abc-chart-region-emphasize-maybe (car region) (cdr region) terse))))
  466.  
  467. ;; 'Twould be nice if this did the hyphenization hacks, too, but it DOESN'T!
  468. ;; (and probably shouldn't, given its calling pattern -- too hard to undo).
  469. (defun abc-chart-region-emphasize-maybe (start end &optional terse)
  470.   "Print the sum of the ABC chart values of the characters in the region; if
  471. = 22, then emphazise it."
  472.   ;; This doesn't really need to be interactive.
  473.   ;; (interactive "r")
  474.   (let* ((region-string (buffer-substring start end))
  475.      (region-abc-value (abc-chart-string region-string)))
  476.     (if (word-22-value-p region-abc-value)
  477.     ;; could also add quotes
  478.     (upcase-region start end))
  479.     (message (if terse
  480.          "%s = %d"
  481.            "%s = %d on the ABC chart.")
  482.          region-string (abc-chart-string region-string))))
  483.  
  484.  
  485. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  486. ;;; "Love22" a region or buffer
  487. ;;;
  488.  
  489. ;;; Beware adding a hypen on the front or end of a hyphenized word, though 
  490. ;;; that isn't so great a tragedy.
  491.  
  492.  
  493.  
  494. ;;; algorithm:
  495. ;; maintain a point before which nothing will be done.
  496. ;; maintain a list of lists of ABC values of the following words,
  497. ;;     and the words themselves (perhaps in reverse order?), and points.
  498. ;;   * sublists in reverse order:  easier to look at bigger & bigger prefixes
  499. ;;   * but we shouldn't be looking at more than a couple of words that way, and
  500. ;;     the "correct" direction is more intuitive.
  501. ;;     can use mapcar to compute sums:  expensive but easy.  (no, hard to look
  502. ;;        at smaller prefixes first).
  503. ;; 
  504. ;; get next word, compute ABC value(s).
  505. ;;   * if any is 22 or multiple thereof, win
  506. ;;   * check each with prefixes of the lists; if any add up to 22 or mult, win.
  507. ;;   * prune the lists
  508.  
  509. (defun love22-region (start end)
  510.   "Call 22-hook on the words in the region whose ABC chart values are 22."
  511.   (interactive "r")
  512.   
  513.   ;; get rid of the buffer, if it's there.
  514.   (debug-form
  515.    (if (get-buffer "*debug*")
  516.        (debug-form (kill-buffer (get-buffer "*debug*")))))
  517.  
  518.   (with-love22-syntax-table
  519.    (save-excursion
  520.      (save-restriction
  521.        (narrow-to-region start end)
  522.        (goto-char (point-min))
  523.        
  524.        (setq love22-traces nil)
  525.        
  526.        ;; while we can find the start of a new word
  527.        (while (re-search-forward "\\<" nil t)
  528.      (setq word-start (point))
  529.      (forward-word 1)
  530.      (setq word-end (point))
  531.      (setq word (buffer-substring word-start word-end))
  532.      
  533.      (check-for-22 word word-start)
  534.      )
  535.        ))))
  536.  
  537. (defun love22-buffer ()
  538.   "Call 22-hook on the words in the buffer whose ABC chart values are 22.
  539. If narrowing is in effect, only affects the accessible region."
  540.   (interactive)
  541.   (love22-region (point-min) (point-max)))
  542.  
  543. ;; There are two distinct criteria:  one for individual words, and another
  544. ;; for sequences of words.  For instance, by default we let words have any
  545. ;;  multiple of 22, but avoid long sequences by prohibiting multi-word 
  546. ;; sequences from having such very large multiples.
  547.  
  548. (defun word-22-value-p (value)
  549.   (and (zerop (mod value 22))
  550.        (not (zerop value))))
  551.  
  552. (defun trace-22-value-p (value)
  553.   (or (= 22 value) (= 44 value)))
  554.  
  555. (defun trace-22-too-large (value)
  556.   "Returns t if the value is larger than any of the acceptable 22 values."
  557.   (> value 44))
  558.  
  559.  
  560. ;; I should probably downcase the word first of all, because of suffix
  561. ;; checking later.  As it is, I'm doing that twice:  once for homonym
  562. ;; (conversion) checking and once for root checking.
  563.  
  564. (defun check-for-22 (word word-start)
  565.   "Checks WORD (and combinations ending with WORD) for ABC value of 22.
  566. Uses homonyms, checks for roots, searches backward through traces.
  567. WORD-START is the start of the word; point is on the end of the word.
  568. Calls 22-hook on any 22-word or sequence ending with word.
  569. If none is found, updates love22-traces.
  570. Doesn't return a useful value."
  571.   
  572.   (debug-form
  573.    (prin1 "check-for-22:") (terpri)
  574.    (prin1 " ") (prin1 word) (prin1 " ") (prin1 word-start) (terpri)
  575.    (prin1 " ") (prin1 love22-traces) (terpri))
  576.  
  577.   ;; possibilities is a list of (continuep . description) pairs.
  578.   ;; found-22 is a cons of (abc-value . trace)
  579.   (let* ((possibilities nil)
  580.      (found-22
  581.       (catch 'found-22
  582.        
  583.        (let* ((permit-continuation
  584.            (not (or
  585.              ;; end of sentence
  586.              (looking-at sentence-end)
  587.              ;; two newlines before next word
  588.              (looking-at "\\W*\n\\W*\n")
  589.              ;; It would be nice to permit this if the whole quote
  590.              ;; or parenthesis were included, but not otherwise...
  591.              ;; end of a quote or parenthesis, or colon
  592.              (looking-at "\"\\|''\\|)\\|:")
  593.              ;; beginning of a quote or parenthesis
  594.              (looking-at ",?\\s *\\(\"\\|``\\|(\\)")
  595.              ;; Possibly prohibit coalescing after commas,
  596.              ;; but that doesn't seem like such a tragedy.
  597.              )))
  598.          
  599.           ;; ORIGINAL WORD
  600.           (word-abc-value (abc-chart-string word))
  601.           (word-description (make-description
  602.                      word-abc-value word-start nil nil)))
  603.          (if (word-22-value-p word-abc-value)
  604.          (throw 'found-22 (cons word-abc-value (list word-description))))
  605.          (if (not (trace-22-too-large word-abc-value))
  606.          (setq possibilities (list (cons permit-continuation
  607.                          word-description))))
  608.          
  609.          ;; CONVERSION
  610.          (let ((cword (cdr (assoc (downcase word) love22-conversion-alist))))
  611.            (if cword
  612.            (let* ((cword-abc-value (abc-chart-string cword))
  613.               (cword-description (make-description
  614.                           cword-abc-value word-start nil 
  615.                           cword)))
  616.              (if (word-22-value-p cword-abc-value)
  617.              (throw 'found-22 (cons cword-abc-value
  618.                         (list cword-description))))
  619.              (if (not (trace-22-too-large cword-abc-value))
  620.              (nconc possibilities
  621.                 (list (cons permit-continuation
  622.                         cword-description))))))))
  623.  
  624.        ;; ROOT
  625.        (let ((root (word-root word)))
  626.          (if root
  627.          (let* ((root-abc-value (abc-chart-string root))
  628.             (root-description (make-description
  629.                        root-abc-value word-start
  630.                        (length root) nil)))
  631.            (if (word-22-value-p root-abc-value)
  632.                (throw 'found-22 (cons root-abc-value
  633.                           (list root-description))))
  634.            ;; Perhaps don't allow coalescing with roots at all.
  635.            (if (not (trace-22-too-large root-abc-value))
  636.                (nconc possibilities
  637.                   (list (cons nil root-description)))))))
  638.        
  639.        (debug-form (prin1 " final possibilities: ")
  640.                (prin1 possibilities) (terpri))
  641.  
  642.        ;; now the word, its conversion, and its root are in possibilities
  643.        (let ((old-traces love22-traces))
  644.          (setq love22-traces nil)
  645.          
  646.          (while possibilities
  647.            (love22-coalesce (car possibilities) old-traces)
  648.            (setq possibilities (cdr possibilities))))
  649.  
  650.        ;; No 22 was found (else we would have thrown out to 'found-22).
  651.  
  652.        ;; ACTION:  I should now prune love22-traces of duplicates.
  653.        
  654.        ;; end of catch body; default is nil (no 22 found)
  655.        nil))
  656.       )
  657.     ;; body of (let found-22 ...)
  658.     (debug-form (prin1 " found-22 = ") (prin1 found-22) (terpri))
  659.     (if found-22
  660.     (progn (22-hook found-22)
  661.            (setq love22-traces nil)))))
  662.  
  663.  
  664. (defun love22-coalesce (cont-desc old-traces)
  665.   "Try to combine words in the traces into groups with 22-valued ABC values.
  666. CONT-DESC is a (continuep . description) cons; TRACES is a list of traces."
  667.   (debug-form
  668.    (prin1 "love22-coalesce:") (terpri)
  669.    (prin1 " ") (prin1 cont-desc) (terpri)
  670.    (prin1 " ") (prin1 old-traces) (terpri))
  671.  
  672.   (let ((continuep (car cont-desc))
  673.     (description (cdr cont-desc)))
  674.     (if (null old-traces)
  675.     ;; There were no qualifying previous words.
  676.     ;; Start a new trace with only the current description.
  677.     ;; love22-traces might not be nil here if this is a transformation 
  678.     ;; and the original word has already been added.
  679.     (if continuep
  680.         (setq love22-traces
  681.           (nconc love22-traces (list (list description)))))
  682.       ;; There were previous words.
  683.       (let (new-traces
  684.         this-trace
  685.         tt-remainder        ; poor variable name choice
  686.         tt-sum)
  687.     (while old-traces
  688.       ;; this-trace exists to be modified and returned (thrown to 'found-22).
  689.       ;; I should be able to do this more efficiently 
  690.       ;;   (eg only copy what's needed, not everything).
  691.       ;; This copy is because of the setcdr below, for truncating traces.
  692.       ;; (Alternately, I could always simply add to traces, and lose them
  693.       ;; only after a 22 or at the end of a sentence.  Hmmmm... seems fishy.)
  694.       (setq this-trace (cons description (copy-sequence (car old-traces)))
  695.         tt-remainder (cons nil this-trace)
  696.         tt-sum 0)
  697.       ;; tt-remainder lags behind those elements of this-trace actually
  698.       ;; examined, so this while loop's condition is "while this-trace has
  699.       ;; unexamined elements".  This won't bail out on the first time
  700.       ;; through because we wouldn't have gotten here if the ABC value of
  701.       ;; the first word were too large.
  702.       (while (cdr tt-remainder)
  703.         
  704.         (debug-form
  705.          (prin1 " tt-remainder = ") (prin1 tt-remainder) (terpri))
  706.  
  707.         ;; Add in the next description's ABC value.
  708.         (setq tt-sum
  709.           (+ tt-sum (description-abc-value (car (cdr tt-remainder)))))
  710.         (if (trace-22-too-large tt-sum)
  711.         ;; If this value is too large, then we will never need to look 
  712.         ;; beyond the current value, so ditch everything after it.
  713.         (setcdr tt-remainder nil)
  714.           (progn
  715.         (setq tt-remainder (cdr tt-remainder))
  716.         ;; Now tt-remainder is the last description summed into tt-sum.
  717.         (if (trace-22-value-p tt-sum)
  718.             (progn
  719.               ;; Delete everything not contributing to tt-sum.
  720.               (setcdr tt-remainder nil)
  721.               ;; Return the (modified) trace.
  722.               (throw 'found-22 (cons tt-sum this-trace))))))
  723.         ;; this was neither a 22-value nor too large; try again
  724.         )  ; end (while (cdr tt-remainder) ...)
  725.       ;; no 22-value found; save away this-trace
  726.       (if continuep
  727.           (setq new-traces (nconc new-traces (list this-trace))))
  728.       (setq old-traces (cdr old-traces))
  729.       )  ; end (while old-traces ...)
  730.  
  731.     (if continuep
  732.         (setq love22-traces (nconc love22-traces new-traces)))
  733.     )  ; end (let new-traces ...)
  734.       )))
  735.  
  736.  
  737. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  738. ;;; Word roots
  739. ;;;
  740.  
  741. ;;; Finding roots may backfire if a root gets connected with something
  742. ;;; previous instead of the whole word getting connected with something later.
  743. ;;; That is, "FOO BARism baz" might be less desirable than foo BARISM BAZ".  Of
  744. ;;; course, BARISM is always checked before BARism; likewise with coalescing.
  745.  
  746. ;;; Finding word roots seems like a hard problem.  I'm giving up on most of
  747. ;;; it for now.
  748. ;;; A future version could return a number of progressively smaller roots.
  749.  
  750. ;;; ACTION:  This suffix checking is VERY inefficient.  Fix it later.
  751.  
  752. (defun word-root (word)
  753.   "Returns a root for WORD, or nil if none is found.
  754. Nasty heuristic."
  755.   
  756.   (setq word (downcase word))
  757.  
  758.   (setq suffix-characters
  759.     (cond
  760.      ((and (suffixp word "ly")
  761.            ;; b: all "ble"
  762.            ;; f: last syllable is "fly"
  763.            ;; i: roots end in "y"
  764.            (not (memq (elt word (- (length word) 3)) '(?b ?f ?i)))
  765.            (not (string-member word love22-ly-exceptions)))
  766.       2)
  767.      ((and (suffixp word "n't")
  768.            (not (string-member word love22-nt-exceptions)))
  769.       3)
  770.      ((suffixp word "'s")
  771.       2)
  772.      ;; Maybe I don't need any silent-e check (eg consecutive consonants).
  773.      ((suffixp word "es")
  774.       (debug-form (prin1 "ES-check: ") (prin1 word)
  775.               (prin1 (string-member word love22-es-exceptions))
  776.               (terpri))
  777.       (if (string-member word love22-es-exceptions)
  778.           nil
  779.         ;; otherwise:
  780.         (let ((sans-es (substring word 0 (- (length word) 2))))
  781.           (if (suffixp sans-es "i")
  782.           nil
  783.         (if (and (apply 'or-fn
  784.                 (mapcar
  785.                  (function
  786.                   (lambda (suffix) (suffixp sans-es suffix)))
  787.                  love22-es-plural-suffixes))
  788.              (not (string-member word love22-es-s-exceptions)))
  789.             2
  790.           1)))))
  791.      ((and (suffixp word "s")
  792.            ;; avoid double-counting possessives
  793.            (not (suffixp word "'s"))
  794.            ;; i: root could end in "y"
  795.            ;; s: "mess", "watercress"
  796.            ;; v: root could end in "f" or "fe"
  797.            (not (memq (elt word (- (length word) 2)) '(?i ?s ?v)))
  798.            (not (string-member word love22-s-exceptions))
  799.            ;; bweare of "ous" adjectives
  800.            (or (not (suffixp word "ous"))
  801.            (string-member word love22-ous-exceptions)))
  802.       1)
  803.      ((and (suffixp word "ism")
  804.            (not (string-member word love22-ism-exceptions)))
  805.       3)
  806.      ((and (suffixp word "ist")
  807.            (not (string-member word love22-ist-exceptions)))
  808.       3)
  809.      ;;    ;; these might be too erratic for use.
  810.      ;;    ((suffixp word "ing")
  811.      ;;     ;; beware doubled letter:  travelling vs traveling (both OK)
  812.      ;;     ;; beware "baking"
  813.      ;;     (setq word-root (allbutlastthreechars word)))
  814.      ;;    ((past-form word)
  815.      ;;     ;;; for instance, "<doubled consonant>ed"
  816.      ;;     (foo))
  817.      ) ; end of cond
  818.     )
  819.   (debug-form (princ " suffix-chars: ") (prin1 suffix-characters) (terpri))
  820.   (if suffix-characters
  821.       (substring word 0 (- (length word) suffix-characters))))
  822.  
  823. (defun suffixp (word suffix)
  824.   "Returns t if SUFFIX is a suffix of WORD, nil otherwise."
  825.   (let ((suffix-len (length suffix))
  826.     (word-len (length word)))
  827.     (and (< suffix-len word-len)
  828.      (string-equal suffix (substring word (- word-len suffix-len))))))
  829.  
  830.  
  831. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  832. ;;; 22-hook
  833. ;;;
  834.  
  835. ;; What a mess!
  836.  
  837. ;; ACTION: problem with redoing when there's an (= 44) there already which
  838. ;; isn't noticed or is coalesced with something else.
  839.  
  840. ;; This should act intelligently depending on intervening text (eg newlines).
  841.  
  842. ;;  "Can insert quotes, upcase, change spaces to hyphens, etc."
  843.  
  844. ;; * upcase
  845. ;; * surround with "" (add extra space if already "")
  846. ;; * spaces to hyphens, if any substitutions done
  847. ;;      . be thoughtful about punctuation (eg don't, if colon or hyphen)
  848. ;;      . remember about newlines, reinsert elsewhere (need a global var)
  849.  
  850. ;; work from end back to avoid screwing up points?
  851. ;; just use word-motion commands to find next word?  No, because of roots (?).
  852. ;; perhaps to deal with roots we need another slot in the description.
  853. ;; if the latter, beware roots getting messed up.
  854. (defun 22-hook (abc-val-trace)
  855.   "The argument is an (abc-value . trace) cons."
  856.   (debug-form
  857.    (prin1 "22-hook:") (terpri)
  858.    (prin1 " ") (prin1 abc-val-trace) (terpri))
  859.  
  860.   (with-love22-syntax-table
  861.    (let* ((abc-value (car abc-val-trace))
  862.       ;; Put 22-trace in forward order.
  863.       (22-trace (nreverse (cdr abc-val-trace)))
  864.       (convert-spaces
  865.        ;; at least two words in this trace, and some word was transformed.
  866.        (and (cdr 22-trace)
  867.         (apply 'or-fn (mapcar 'description-text-fn 22-trace))))
  868.       ;; (region-start (description-start (car 22-trace)))
  869.       ;; (region-end (description-end (car (cdr (last 22-trace)))))
  870.       (missing-newline nil)
  871.       (trace-start (description-start (car 22-trace))))
  872.      
  873.      (debug-form 
  874.       (prin1 " convert = ") (prin1 convert-spaces) (terpri))
  875.  
  876.      ;; Remember where I was.
  877.      ;; Is this not very useful, in the case in which characters are inserted 
  878.      ;; or deleted?  Perhaps the best thing is just to leave point at the end 
  879.      ;; of the 22-region, or at the beginning of the first word after, or at 
  880.      ;; the end of the last word even partially contained in it
  881.      ;; (save-excursion ...)
  882.      
  883.      ;; go to beginning of region.
  884.      (goto-char trace-start)
  885.      
  886.      ;; invariant:  at the beginning/end of this loop, I'm at the beginning
  887.      ;; of the word corresponding to the first description.
  888.      (while (cdr 22-trace)
  889.        (if (description-text (car 22-trace))
  890.        (progn (kill-word 1)
  891.           (insert (upcase (description-text (car 22-trace)))))
  892.      (upcase-word 1))
  893.        ;; (setq end-of-prev-word (point))
  894.        ;; (re-search-forward "\\<")
  895.        ;; (setq start-of-next-word (point))
  896.        
  897.        ;; Deal with interword stuff.
  898.        (if convert-spaces 
  899.        (cond ((looking-at "\\s *\\<")
  900.           ;; only whitespace before beginning of next word, no
  901.           ;; punctuation:  convert to a hyphen with no surrounding
  902.           ;; space
  903.           (let ((end-of-prev-word (point)))
  904.             (re-search-forward "\\<")
  905.             (setq missing-newline 
  906.               (or missing-newline
  907.                   (string-match "\n" (buffer-substring
  908.                           end-of-prev-word
  909.                           (point)))))
  910.             (delete-region end-of-prev-word (point))
  911.             (insert "-")))
  912.          ;; could have other cases here
  913.  
  914.          ;; default -- leave punctuation as is.
  915.          (t
  916.           (re-search-forward "\\<")))
  917.      ;; Leave interword space alone, but move to beginning of next word.
  918.      ;; Here perhaps I should close up too-large spaces, etc.  Nah, that's 
  919.      ;; for the typist to do.
  920.      (re-search-forward "\\<"))
  921.        
  922.  
  923.        (setq 22-trace (cdr 22-trace))
  924.        )
  925.      
  926.      ;; Upcase last word, which may be a root.
  927.      ;; This special code isn't necessary if we prohibit coalescing with roots.
  928.      (let ((last-desc (car 22-trace)))
  929.        (if (description-rootp last-desc)
  930.        (let ((word-start (point)))
  931.          (forward-char (description-length last-desc))
  932.          (debug-form
  933.           (prin1 " forward ") (prin1 (description-length last-desc))
  934.           (prin1 " from ") (prin1 (char-to-string (preceding-char)))
  935.           (prin1 (char-to-string (following-char))) (terpri))
  936.          (upcase-region word-start (point)))
  937.      (if (description-text (car 22-trace))
  938.          (progn (kill-word 1)
  939.             (insert (upcase (description-text (car 22-trace)))))
  940.        (upcase-word 1))))
  941.      
  942.      ;; Add surrounding quotes at trace-start and at point.
  943.      ;; If the region is already surrounded by quotes, don't add any more.
  944.      (if (not (quoted-region-p trace-start (point)))
  945.        (let ((inserted-characters 0)
  946.          (trace-end (point)))
  947.      (goto-char trace-start)
  948.      (setq inserted-characters (insert-after-point
  949.                     (if (TeX-mode-p) "``" "\"")))
  950.      (setq inserted-characters (+ inserted-characters
  951.                       (add-quote-separation-maybe)))
  952.      (goto-char trace-end)
  953.      (forward-char inserted-characters)
  954.      (insert (if (TeX-mode-p) "''" "\""))))
  955.  
  956.      ;; Add sum
  957.      (if (and (not (= 22 abc-value))
  958.           love22-explicit-sum)
  959.      (progn 
  960.        (if (not (looking-at "\\w"))
  961.            (insert " "))
  962.        ;; could use "n * 22" instead of "44" "66", etc.
  963.        (insert "(= " (int-to-string abc-value) ")")))
  964.        
  965.      ;; Add newline
  966.      (if missing-newline 
  967.      (progn (insert "\n")
  968.         (fixup-whitespace)))
  969.      
  970.      ;; Add quote separation
  971.      (add-quote-separation-maybe)
  972.  
  973.      ;; Skip over the rest of word if this was a prefix.
  974.      (if (description-rootp (car 22-trace))
  975.      (re-search-forward "\\>"))
  976.  
  977.      ;; if sum wasn't 22 (eg was 44), then point this out:  insert 
  978.      ;; " (= 2*22)"
  979.      
  980.      )
  981.  
  982.    )
  983.   )
  984.  
  985.  
  986. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  987. ;;; Love22 minor mode
  988. ;;;
  989.  
  990. ;; problem:  minibuffer seems to use map which causes it to annoyingly
  991. ;; write over minibuffer input.  (Well, it goes away after a second.)
  992. ;;  Soln:  (1) fix this (2) put info in mode line
  993.  
  994. ;; consider putting info in mode line instead.
  995.  
  996. ;;; I should change the current syntax table into a full syntax table if
  997. ;;; it's currently sparse, else I'll spend too much time looking up characters
  998. ;;; in it (and describe-bindings will be ugly).
  999.  
  1000. (defun love22-mode (arg) 
  1001.   "Minor mode to support writing of Love22 text.
  1002. Toggle love22-mode, or turn it on if optional ARG is positive.
  1003.  
  1004. Love22 mode causes the display of the ABC chart value value of the current
  1005. word, as well as emphasizing words whose value is a multiple of 22, if 
  1006. love22-emphasize is non-nil.  (This mode's emphasis functionality is
  1007. considerably less than that of love22-buffer, but it operates in real time."
  1008.  
  1009.   (interactive "P")
  1010.   (if love22-mode
  1011.       (love22-restore))
  1012.   (setq love22-mode
  1013.     (if (null arg) (not love22-mode)
  1014.       (> (prefix-numeric-value arg) 0)))
  1015.   (if love22-mode
  1016.       (love22-overlay))
  1017.  
  1018.   (set-buffer-modified-p (buffer-modified-p)))
  1019.  
  1020. ;; Adapted from gin-mode
  1021. (if (boundp 'love22-mode)
  1022.     ()
  1023.   (setq minor-mode-alist (cons '(love22-mode " Love22") 
  1024.                    minor-mode-alist))
  1025.   (make-variable-buffer-local 'love22-mode)
  1026.   (set-default 'love22-mode nil)
  1027.   (fset 'old-kill-all-local-variables
  1028.     (symbol-function 'kill-all-local-variables))
  1029.   ;;   (make-variable-buffer-local 'gin-left-hang-indent-re)
  1030.   ;;   (make-variable-buffer-local 'gin-retain-indent-re)
  1031.   )
  1032.  
  1033.  
  1034. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1035. ;;; Love22-new-command
  1036. ;;;
  1037.  
  1038. ;;; This is used to create new functions when the function cell definition
  1039. ;;; is being modified or a wholly new command is being substituted into a
  1040. ;;; keymap.
  1041.  
  1042. (defun lambda-list-argnames-from-iarg (iarg)
  1043.   "Returns a cons of (lambda-list . argnames), given the interactive spec."
  1044.   ;; Strip the leading asterisk, if any.
  1045.   ;; If the string is empty, this will err.  It deserves to err.
  1046.   (if (char-equal (elt iarg 0) ?*)
  1047.       (setq iarg (substring iarg 1)))
  1048.   (cond ((or (string-equal "p" iarg)
  1049.          (string-equal "P" iarg))
  1050.      (cons '(&optional n) '(n)))
  1051.     ((string-equal "p\nP" iarg)
  1052.      (cons '(a &optional b) '(a b)))
  1053.     ((string-equal "Op" iarg)
  1054.      (cons '(p-arg &optional P-arg)
  1055.            '((if (or P-arg (not (= 1 p-arg))) p-arg))))
  1056.     (t
  1057.      (error "Unrecognized interactive spec %s." iarg))))
  1058.  
  1059. ;; This works when interpreted, but seems to be having some trouble
  1060. ;; during byte-compilation.
  1061. (defmacro love22-new-command (int-arg old-command)
  1062.   "INT-ARG is the argument to interactive in OLD-COMMAND."
  1063.   ;; Why did this work before I had the eval??
  1064.   ;; Why does it work now??  (Well, it doesn't...)
  1065.   (let* ((oc (eval old-command))
  1066.      (ia (eval int-arg))
  1067.      (ll-an (lambda-list-argnames-from-iarg ia)))
  1068.     ;; This could be generalized.
  1069.     (if (string-equal ia "Op")
  1070.     (setq ia "p\nP"))
  1071.     (` (function 
  1072.     (lambda 
  1073.       (, (car ll-an))
  1074.       (, (concat "See " (symbol-name oc) " for more documentation."
  1075.              "\nThis implementation also calls abc-chart-word."))
  1076.       (interactive (, ia))
  1077.       (, (cons oc (cdr ll-an)))
  1078.       (abc-chart-word t t))))))
  1079.  
  1080.  
  1081. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1082. ;;; Overlay and restore by modifying function cell binding
  1083. ;;;
  1084.  
  1085. ;; Which map contains these doesn't matter, since the function definition
  1086. ;; is being directly changed.
  1087.  
  1088. ;; ACTION:  Sometimes these don't seem to stick.
  1089. (defvar love22-cell-commands
  1090.   '(("p"
  1091.      previous-line next-line
  1092.      forward-word backward-word
  1093.      newline
  1094.      beginning-of-line end-of-line
  1095.      backward-sentence forward-sentence
  1096.      kill-word backward-kill-word
  1097.      )
  1098.     ("Op"
  1099.      scroll-up scroll-down
  1100.      )
  1101.     ("*P" 
  1102.      transpose-chars
  1103.      )
  1104.     ("*p\nP"
  1105.      delete-char delete-backward-char backward-delete-char-untabify
  1106.      )
  1107.     )
  1108.   "Commands whose definitions are modified by love22-mode.
  1109. This is a list of (interactive-arg . list-of-command-symbols) conses.
  1110.  
  1111. The keymap can remain unchanged; the new definition is used.  When love22-mode
  1112. is exited, the original definition is restored to the command.
  1113.  
  1114. The \"Op\" specification is special:  it's for \"p\" functions which should be
  1115. called with nil if there was no argument explicitly supplied.")
  1116.  
  1117. (defun love22-cell-overlay-commands (iarg-commands)
  1118.   "IARG-COMMANDS is a member of love22-cell-commands.
  1119. love22-cell-overlay-command is called on each of the commands."
  1120.   (let ((iarg (car iarg-commands)))
  1121.     (mapcar (function (lambda (command)
  1122.             (love22-cell-overlay-command iarg command)))
  1123.         (cdr iarg-commands))))
  1124.  
  1125. (defun love22-cell-overlay-command (interactive-arg-cell command)
  1126.   "Adds to the function definition of symbol COMMAND a call to abc-chart-word."
  1127.   (let* ((name (symbol-name command))
  1128.      (symbol-with-old (intern (concat "old-" name))))
  1129.     ;; (fset symbol-with-old (symbol-function command))
  1130.     (fset symbol-with-old (symbol-function command))
  1131.     
  1132.     ;; I build these functions anew each time the mode is invoked so that the 
  1133.     ;; most recent version is obtained (someone else might have been fiddling
  1134.     ;; with the function definitions, too); if love22-mode is invoked last
  1135.     ;; and turned off first, then this won't interfere with other such modes.
  1136.     
  1137.     (fset command (love22-new-command interactive-arg-cell symbol-with-old))))
  1138.  
  1139.  
  1140. ;; ACTION:  This could check whether the defn has changed since it was set
  1141. ;; by love22-mode.  If so, complain, or something.
  1142. ;; This might require more space (eg storing the love22 definitions of the 
  1143. ;; commands so there's something to check against).
  1144.  
  1145. (defun love22-cell-restore-commands (iarg-commands)
  1146.   "IARG-COMMANDS is a member of love22-cell-commands.
  1147. love22-cell-restore-command is called on each of the commands."
  1148.   (mapcar 'love22-cell-restore-command
  1149.       (cdr iarg-commands)))
  1150.  
  1151. ;; This doesn't have to worry about the interactive specification.
  1152. (defun love22-cell-restore-command (command)
  1153.   "Restores the original value of the function definition of symbol COMMAND."
  1154.   (fset command
  1155.     (symbol-function (intern (concat "old-" (symbol-name command))))))
  1156.  
  1157.  
  1158. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1159. ;;; Overlay and restore by replacing in keymap
  1160. ;;;
  1161.  
  1162. ;; We can't just do
  1163. ;; (fset 'old-self-insert-command (symbol-function 'self-insert-command))
  1164. ;; (fset 'self-insert-command (symbol-function 'love22-self-insert-command))
  1165. ;; as above, because Emacs special-cases when it sees self-insert-command
  1166. ;; in a keymap.
  1167.  
  1168. (defvar love22-map-commands
  1169.   '(("p"
  1170.      self-insert-command
  1171.      forward-char backward-char
  1172.      )
  1173.     )
  1174.   "Commands replaced in the keymap by love22-mode.
  1175. This is a list of (interactive-arg . list-of-command-symbols) conses.
  1176.  
  1177. This is because when Emacs sees these commands, rather than looking up
  1178. their current definitions, it uses the original version coded in C.  Thus,
  1179. in order to use a new definition, the keymap must be changed.  When
  1180. love22-mode is exited, the original command is replaced in the keymap.
  1181.  
  1182. self-insert-command is an example of such a function, but it is also
  1183. treated somewhat specially.")
  1184.  
  1185. (defun love22-map-overlay-commands (iarg-commands)
  1186.   "IARG-COMMANDS is a member of love22-map-commands.
  1187. love22-map-overlay-command is called on each of the commands."
  1188.   (let ((iarg (car iarg-commands)))
  1189.     (mapcar (function (lambda (command)
  1190.             (love22-map-overlay-command iarg command)))
  1191.         (cdr iarg-commands))))
  1192.  
  1193. (defun love22-map-overlay-command (interactive-arg-map overlaid-command)
  1194.   "Changes instances of COMMAND in the keymaps to the love22 version.
  1195.  
  1196. If the command is found in the global keymap, then the love22-symbol is
  1197. placed in the local keymap, shadowing it.  If the command is found in the
  1198. local keymap, then it is replaced by love22-local-symbol."
  1199.  
  1200.   (let* ((name (symbol-name overlaid-command))
  1201.      (love22-symbol (intern (concat "love22-" name)))
  1202.      (love22-local-symbol (intern (concat "love22-local-" name))))
  1203.  
  1204.     (fset love22-symbol (love22-new-command interactive-arg-map 
  1205.                         overlaid-command))
  1206.     (fset love22-local-symbol love22-symbol)
  1207.     
  1208.     (debug-form
  1209.      (with-output-to-temp-buffer
  1210.      (concat "*" name " definition")
  1211.        (print love22-symbol)
  1212.        ;; (print (love22-new-command overlaid-command))
  1213.        (print (symbol-function love22-symbol))))
  1214.     
  1215.     ;; First, deal with command found in the local keymap.
  1216.     (substitute-key-definition overlaid-command
  1217.                    love22-local-symbol
  1218.                    (current-local-map))
  1219.     ;; Second, shadow instances of command visible in the global keymap.
  1220.     (mapcar (function (lambda (key)
  1221.             (if (not (local-key-binding key))
  1222.                 (local-set-key key love22-symbol))))
  1223.         ;; This checks the global keymap only.
  1224.         (where-is-internal overlaid-command))))
  1225.  
  1226. (defun love22-map-restore-commands (iarg-commands)
  1227.   "IARG-COMMANDS is a member of love22-map-commands.
  1228. love22-map-restore-command is called on each of the commands."
  1229.   (mapcar 'love22-map-restore-command
  1230.       (cdr iarg-commands)))
  1231.  
  1232. (defun love22-map-restore-command (command)
  1233.   "Restores the original value of the function definition of symbol COMMAND.
  1234.  
  1235. When we undo the Love22 bindings, if the love22-local version is found in
  1236. the local keymap, the command replaces it there; otherwise, the local
  1237. keymap was originally empty (the command was found in the global keymap),
  1238. so the slot in the local keymap is simply unset and the key's binding will
  1239. henceforth be found in the global keymap, as desired."
  1240.  
  1241.   (let* ((name (symbol-name command))
  1242.      (love22-symbol (intern (concat "love22-" name)))
  1243.      (love22-local-symbol (intern (concat "love22-local-" name))))
  1244.     (mapcar 'local-unset-key
  1245.         (where-is-internal love22-symbol
  1246.                    (current-local-map)))
  1247.     (substitute-key-definition 'love22-local-symbol
  1248.                    command
  1249.                    (current-local-map))))
  1250.  
  1251. ;; Two distinct commands are substituted for self-insert-command; these
  1252. ;; functions overlay and undo the emphasized versions in a fashion very
  1253. ;; much like that above.
  1254.  
  1255. (defun love22-self-insert-emphasize-maybe (n)
  1256.   "Like self-insert, but also calls abc-chart-word-emphasize-maybe.
  1257. This needs to be interactive because it gets bound to keys."
  1258.   (interactive "p")
  1259.   (self-insert-command n)
  1260.   (if love22-emphasize
  1261.       (abc-chart-word-emphasize-maybe t t)
  1262.     (abc-chart-word t t)))
  1263.  
  1264. (defvar love22-local-self-insert-emphasize-maybe)
  1265. (fset 'love22-local-self-insert-emphasize-maybe
  1266.       'love22-self-insert-emphasize-maybe)
  1267.  
  1268. ;; sei = self-insert-emphasize
  1269. (defun love22-sie-overlay ()
  1270.   "Substitute love22-self-insert-emphasize-maybe for self-insert-command."
  1271.  
  1272.   ;; Make punctuation possibly emphasize the preceding word;  maintain 
  1273.   ;; distinction between self-insert-command in local and global maps.
  1274.   (mapcar (function
  1275.        (lambda (key)
  1276.          (let ((key-binding (local-key-binding key)))
  1277.            (if (eq key-binding 'self-insert-command)
  1278.            (local-set-key key
  1279.                   'love22-local-self-insert-emphasize-maybe)
  1280.          (if (and (not key-binding)
  1281.               (eq (global-key-binding key) 'self-insert-command))
  1282.              (local-set-key key
  1283.                     'love22-self-insert-emphasize-maybe))))))
  1284.       '("." "!" "?" " " "," ";" ":" "-" "/"))
  1285.   )
  1286.  
  1287. (defun love22-sie-restore ()
  1288.   "Substitute self-insert-command for love22-self-insert-emphasize-maybe.
  1289. This undoes the effect of love22-sie-overlay."
  1290.  
  1291.   ;;   (mapcar 'local-unset-key
  1292.   ;;       (where-is-internal 'love22-self-insert-command
  1293.   ;;                  (current-local-map)))
  1294.   (mapcar 'local-unset-key
  1295.       (where-is-internal 'love22-self-insert-emphasize-maybe
  1296.                  (current-local-map)))
  1297.   ;;   (substitute-key-definition 'love22-local-self-insert-command
  1298.   ;;                  'self-insert-command
  1299.   ;;                  (current-local-map))
  1300.   (substitute-key-definition 'love22-local-self-insert-emphasize-maybe
  1301.                  'self-insert-command
  1302.                  (current-local-map)))
  1303.  
  1304.  
  1305. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1306. ;;; Overlay and restore -- top-level functions
  1307. ;;;
  1308.  
  1309. ;; ACTION:  the old-*-binding variables don't seem to be doing their job;
  1310. ;; \C-x= and \M-= stay bound to abc-chart-word and abc-chart-region.
  1311.  
  1312. (defvar old-c-x=-binding)
  1313. (defvar old-m-=-binding)
  1314. ;; This should only be done once -- perhaps move up to the progn at the top.
  1315. (make-variable-buffer-local 'old-c-x=-binding)
  1316. (make-variable-buffer-local 'old-m-=-binding)
  1317.  
  1318. (defun love22-overlay-keybindings ()
  1319.   (setq old-c-x=-binding (local-key-binding "\C-x="))
  1320.   (setq old-m-=-binding (local-key-binding "\M-="))
  1321.   (local-set-key "\C-x=" 'abc-chart-word)
  1322.   (local-set-key "\M-=" 'abc-chart-region))
  1323.  
  1324. (defun love22-restore-keybindings ()
  1325.   (local-set-key "\C-x=" old-c-x=-binding)
  1326.   (local-set-key "\M-=" old-m-=-binding))
  1327.  
  1328. (defun love22-overlay ()
  1329.   "Substitutes new definitions for many commands.
  1330. Also, for each key bound to self-insert-command, places a binding in the local
  1331. keymap for love22-self-insert-command.
  1332. Because of this, love22 mode should be the last minor mode added and the
  1333. first removed."
  1334.  
  1335.   ;; If the local keymap is sparse, replace it with a full one.
  1336.   (make-local-keymap-full)
  1337.  
  1338.   ;; nonlocal changes
  1339.   (mapcar 'love22-cell-overlay-commands love22-cell-commands)
  1340.   ;; booby-trap kill-all-local-variables, called when the major mode changes
  1341.   (fset 'kill-all-local-variables
  1342.     (symbol-function 'love22-kill-all-local-variables))
  1343.  
  1344.   ;; local changes
  1345.   (love22-sie-overlay)
  1346.   (mapcar 'love22-map-overlay-commands love22-map-commands)
  1347.   (love22-overlay-keybindings))
  1348.  
  1349. (defun love22-restore-non-local ()
  1350.   "Fixes non-local everything that Love22 mode screwed up."
  1351.   (mapcar 'love22-cell-restore-commands love22-cell-commands)
  1352.   ;; Again, I do this every time because I'm paranoid the value might change.
  1353.   (fset 'kill-all-local-variables
  1354.     (symbol-function 'old-kill-all-local-variables)))
  1355.  
  1356. (defun love22-restore ()
  1357.   (love22-restore-non-local)
  1358.   ;; restore local changes
  1359.   (love22-sie-restore)
  1360.   (mapcar 'love22-map-restore-commands love22-map-commands)
  1361.   (love22-overlay-keybindings))
  1362.  
  1363. (defvar old-kill-all-local-variables nil
  1364.   "Keeps the true kill-all-local-variables function during Love22 mode.")
  1365.  
  1366. (defun love22-kill-all-local-variables ()
  1367.   (love22-restore-non-local)
  1368.   (kill-all-local-variables))
  1369.  
  1370. (defun make-local-keymap-full ()
  1371.   "If the local keymap is sparse, replace it with a full one."
  1372.   (let ((old-local-keymap (current-local-map)))
  1373.     (if (not (vectorp old-local-keymap))
  1374.     (progn
  1375.       (use-local-map (make-keymap))
  1376.       (if old-local-keymap
  1377.           (mapcar (function
  1378.                (lambda (char-binding)
  1379.              ;; Breaking the abstraction barrier would be faster.
  1380.              (local-set-key (char-to-string (car char-binding))
  1381.                     (cdr char-binding))))
  1382.               (cdr old-local-keymap)))))))
  1383.  
  1384.  
  1385. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1386. ;;; Utilities
  1387. ;;;
  1388.  
  1389. ;; In the standard 18.55 distribution, backquote has a bug.
  1390. ;; I don't think this program needs this particular function, though...
  1391. ;; (defun bq-splicequote (form)
  1392. ;;   (setq tailmaker (list form (list 'quote tailmaker)))
  1393. ;;   (setq state 'append))
  1394.  
  1395. (defmacro with-love22-syntax-table (&rest body)
  1396.   (` (let ((current-syntax (syntax-table)))
  1397.        (unwind-protect
  1398.        (progn
  1399.          (set-syntax-table love22-syntax-table)
  1400.          (,@ body))
  1401.      (set-syntax-table current-syntax)))))
  1402.  
  1403. (defvar love22-debugging nil
  1404.   "T if I'm debugging this, nil otherwise.
  1405. This runs very slowly while debugging is ongoing.")
  1406.  
  1407. (defmacro debug-form (&rest body)
  1408.   (if love22-debugging
  1409.       (append '(let ((standard-output (get-buffer-create "*debug*")))) body)))
  1410.  
  1411. ;; Adapted from cl.el's member (which uses eql where I have string-equal).
  1412. (defun string-member (item list)
  1413.   "Find ITEM in LIST; return first link in LIST whose car is string= to ITEM."
  1414.   (let ((ptr list)
  1415.         (done nil)
  1416.         (result '()))
  1417.     (while (not (or done (null ptr)))
  1418.       (cond ((string-equal item (car ptr))
  1419.              (setq done t)
  1420.              (setq result ptr)))
  1421.       (setq ptr (cdr ptr)))
  1422.     result))
  1423.  
  1424. (defun or-fn (&rest args)
  1425.   "Like or, but can be an argument to apply, funcall, mapcar, etc."
  1426.   (if (null args)
  1427.       nil
  1428.     (or (car args) (apply 'or-fn (cdr args)))))
  1429.  
  1430. (defun insert-after-point (string)
  1431.   "Inserts the string after point.  Returns the length of the string."
  1432.   (let ((string-length (length string)))
  1433.     (insert string)
  1434.     (backward-char string-length)
  1435.     string-length))
  1436.  
  1437. (defun TeX-mode-p ()
  1438.   "Returns t if the major mode is a Tex editing mode, nil otherwise."
  1439.   (memq major-mode '(plain-TeX-mode 'LaTeX-mode)))
  1440.  
  1441. ;; ACTION:  This should be more sophisticated:  e.g., in tex-mode, don't add
  1442. ;; anything between { and { or } and }.  Perhaps use a variable settable like
  1443. ;; the surrounding stuff (quotes vs. boldface...).
  1444. (defun add-quote-separation-maybe ()
  1445.   "Returns the number of characters inserted."
  1446.   (if (and (char-equal (preceding-char) (following-char))
  1447.        (not (looking-at "\\s ")))
  1448.       ;; previous two chars are equal and not whitespace
  1449.       (if (TeX-mode-p)
  1450.       (progn (insert "\\,") 2)
  1451.     (progn (insert " ") 1))
  1452.     0))
  1453.  
  1454. ;; ACTION: problem with this when there's an (= 44) there already which
  1455. ;; isn't noticed or is coalesced with something else.
  1456. (defun quoted-region-p (start end)
  1457.   "Returns t if the region is quoted, nil otherwise."
  1458.   ;; As it turns out, this always leaves point at end, where I want it, 
  1459.   ;; but it's best to be safe...
  1460.   (save-excursion
  1461.     (goto-char start)
  1462.     (if (TeX-mode-p)
  1463.     (and
  1464.      (string-equal (buffer-substring (max (point-min) (- start 2)) start)
  1465.                "``")
  1466.      (progn (goto-char end)
  1467.         (looking-at "''"))
  1468.      ;; ACTION:  internal quotes are matched
  1469.      )
  1470.       (and 
  1471.        (not (bobp))
  1472.      (char-equal (preceding-char) ?\")
  1473.      (progn (goto-char end)
  1474.         (char-equal (following-char) ?\"))
  1475.      ;; no internal quotation marks
  1476.      (not (string-match "\"" (buffer-substring start end)))))))
  1477.