home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / modula-3 / m3-3.5 / m3-3 / usr / local / modula3-3.5.4-B / lib / elisp / ispell19-3.0.el next >
Encoding:
Text File  |  1995-11-15  |  56.8 KB  |  1,483 lines

  1. ;Here's a version of ispell.el which works with the old ispell v3
  2. ;rather than the new unimproved version 4 distributed with emacs v19.
  3.  
  4. ;Some big holes still are highlighting in version 19 emacs, and
  5. ;apparently ispell-choose-help doesn't work on all emacs versions.
  6. ;Hopefully I'll look at the v19 highlighting, but if you have
  7. ;suggestions, let me know.  The code in there now has been contributed
  8. ;by various authors, and I haven't tested it.
  9. ;
  10. ;Please send all comments, suggestions, improvements, and bug reports to me
  11. ;at stevens@cpsc.ucalgary.ca  -- or --  stevens@hplkss.hpl.hp.com
  12. ;
  13. ;Thanks        -Ken
  14. ;
  15. ;______________________________cut here_________________________________
  16. ;;;;;;;;;;;;;;;;;;;;;;;;;;; -*- Mode: emacs-lisp -*- ;;;;;;;;;;;;;;;;;;;;;;;;;;
  17. ;;; Spelling correction interface for GNU EMACS "ispell"
  18. ;;; 
  19. ;;; 
  20. ;;; Authors         : Ken Stevens and a cast of thousands.
  21. ;;;                 : Original by Walt Buehring
  22. ;;; Last Modified By: Ken Stevens <stevens@cpsc.ucalgary.ca>
  23. ;;; Last Modified On: Wed Jun 02 12:32:52 MDT 1993
  24. ;;; Update Revision : 2.20
  25. ;;; Syntax          : emacs-lisp
  26. ;;; Status        : Beta test
  27. ;;; Version        : International Ispell Version 3.0 by Geoff Kuenning.
  28. ;;; 
  29. ;;; 
  30.  
  31. ;;; Not yet released....  still testing!
  32.  
  33. (defvar ispell-version "2.20 Wed Jun 02 12:32:52 MDT 1993 ==Kenny7 was here==")
  34.  
  35. ;;; INSTRUCTIONS
  36. ;;;
  37. ;;;  This code contains a section of user-settable variables that you should
  38. ;;; inspect prior to installation.  Look past the end of the history list.
  39. ;;; Set them up for your locale and the preferences of the majority of the
  40. ;;; users.  Otherwise the users may need to set a number of variables
  41. ;;; themselves.
  42. ;;;  You particularly may want to change the default dictionary for your
  43. ;;; country and language.
  44. ;;;
  45. ;;; 
  46. ;;; To fully install this, add this file to your GNU lisp directory and 
  47. ;;; compile it with M-X byte-compile-file.  Then add the following to the
  48. ;;; appropriate init file:
  49. ;;;
  50. ;;;  (autoload 'ispell-word "ispell"
  51. ;;;    "Check the spelling of word in buffer." t)
  52. ;;;  (global-set-key "\e$" 'ispell-word)
  53. ;;;  (autoload 'ispell-region "ispell"
  54. ;;;    "Check the spelling of region." t)
  55. ;;;  (autoload 'ispell-buffer "ispell"
  56. ;;;    "Check the spelling of buffer." t)
  57. ;;;  (autoload 'ispell-complete-word "ispell"
  58. ;;;    "Look up current word in dictionary and try to complete it." t)
  59. ;;;  (autoload 'ispell-change-dictionary "ispell"
  60. ;;;    "Change ispell dictionary." t)
  61. ;;;
  62. ;;; 
  63. ;;; TABLE OF CONTENTS
  64. ;;;
  65. ;;;   ispell-word
  66. ;;;   ispell-region
  67. ;;;   ispell-buffer
  68. ;;;   ispell-complete-word
  69. ;;;   ispell-change-dictionary
  70. ;;;   ispell-kill-ispell
  71. ;;;   ispell-pdict-save
  72. ;;;   
  73. ;;;
  74. ;;; TYPE IN A QUICK TUTORIAL OF THE COMMANDS HERE!
  75. ;;;
  76. ;;; Commands in ispell-region:
  77. ;;; Character replacement: Replace word with choice.  May query-replace.
  78. ;;; ' ': Accept word this time.
  79. ;;; 'i': Accept word and insert into private dictionary.
  80. ;;; 'a': Accept word for this session.
  81. ;;; 'A': Accept word and place in buffer-local dictionary.
  82. ;;; 'r': Replace word with typed-in value.  Rechecked.
  83. ;;; 'R': Replace word with typed-in value. Query-replaced in buffer. Rechecked.
  84. ;;; '?': Show these commands
  85. ;;; 'x': Exit spelling buffer.  Move cursor to original point.
  86. ;;; 'X': Exit spelling buffer.  Leave cursor at the current point.
  87. ;;; 'q': Quit spelling session (Kills ispell process).
  88. ;;; 'l': Look up typed-in replacement in alternate dictionary.  Wildcards okay.
  89. ;;;
  90. ;;;
  91. ;;; BUGS:
  92. ;;;
  93. ;;;   ispell-choose-help doesn't seem to work for all emacs versions.
  94. ;;;   highlighting for version 19 emacs not fully tested or implemented.
  95. ;;;
  96. ;;;
  97. ;;; HISTORY
  98. ;;;
  99. ;;;
  100. ;;; Revision 2.20  1993/06/01 16:47:24  stevens
  101. ;;; Debugging: Boris Aronov, Rik Faith, ....
  102. ;;; Major update including many tweaks.
  103. ;;; Many changes were integrations of suggestions.
  104. ;;; ispell-complete-word originally ported by Ashwin Ram.
  105. ;;; Particular thanks to Michael Lipp, Jamie Zawinski, Phil Queinnec
  106. ;;;  and John Heidemann for suggestions and code.
  107. ;;; lookup-words rehacked to use call-process.
  108. ;;; ispell-complete-word rehacked to be compatible with the rest of the
  109. ;;; system for word searching and to include multiple wildcards,
  110. ;;; and it's own dictionary.
  111. ;;; query-replace capability added.  New options 'X' and 'R'.
  112. ;;; buffer-local modes for dictionary, word-spelling, and formatter-parsing.
  113. ;;; Many random bugs, like commented comments being skipped, fix to
  114. ;;; keep-choices-win, fix for math mode, added pipe mode choice,
  115. ;;; fixed 'q' command, ispell-word checks previous word and leave cursor
  116. ;;; in same location.  Fixed tib code which could drop spelling regions.
  117. ;;; Cleaned up setq calls for efficiency. Gave more context on window overlays.
  118. ;;; Assure context on ispell-choose.  Window lossage in look command fixed.
  119. ;;; Due to pervasive opinion, common-lisp package syntax removed.
  120. ;;;
  121. ;;; Revision 2.19  1992/01/10  10:54:08  geoff
  122. ;;; Make another attempt at fixing the "Bogus, dude" problem.  This one is
  123. ;;; less elegant, but has the advantage of working.
  124. ;;;
  125. ;;; Revision 2.18  1992/01/07  10:04:52  geoff
  126. ;;; Fix the "Bogus, Dude" problem in ispell-word.
  127. ;;;
  128. ;;; Revision 2.17  1991/09/12  00:01:42  geoff
  129. ;;; Add some changes to make ispell-complete-word work better, though
  130. ;;; still not perfectly.
  131. ;;; 
  132. ;;; Revision 2.16  91/09/04  18:00:52  geoff
  133. ;;; More updates from Sebastian, to make the multiple-dictionary support
  134. ;;; more flexible.
  135. ;;; 
  136. ;;; Revision 2.15  91/09/04  17:30:02  geoff
  137. ;;; Sebastian Kremer's tib support
  138. ;;; 
  139. ;;; Revision 2.14  91/09/04  16:19:37  geoff
  140. ;;; Don't do set-window-start if the move-to-window-line moved us
  141. ;;; downward, rather than upward.  This prevents getting the buffer all
  142. ;;; confused.  Also, don't use the "not-modified" function to clear the
  143. ;;; modification flag;  instead use set-buffer-modified-p.  This prevents
  144. ;;; extra messages from flashing.
  145. ;;; 
  146. ;;; Revision 2.13  91/09/04  14:35:41  geoff
  147. ;;; Fix a spelling error in a comment.  Add code to handshake with the
  148. ;;; ispell process before sending anything to it.
  149. ;;; 
  150. ;;; Revision 2.12  91/09/03  20:14:21  geoff
  151. ;;; Add Sebastian Kremer's multiple-language support.
  152. ;;; 
  153. ;;;
  154. ;;; Walt Buehring
  155. ;;; Texas Instruments - Computer Science Center
  156. ;;; ARPA:  Buehring%TI-CSL@CSNet-Relay
  157. ;;; UUCP:  {smu, texsun, im4u, rice} ! ti-csl ! buehring
  158. ;;;
  159. ;;; ispell-region and associated routines added by
  160. ;;; Perry Smith
  161. ;;; pedz@bobkat
  162. ;;; Tue Jan 13 20:18:02 CST 1987
  163. ;;; 
  164. ;;; extensively modified by Mark Davies and Andrew Vignaux
  165. ;;; {mark,andrew}@vuwcomp
  166. ;;; Sun May 10 11:45:04 NZST 1987
  167. ;;; 
  168. ;;; Ken Stevens  ARPA: stevens@cpsc.ucalgary.ca or stevens@hplkss.hpl.hp.com
  169. ;;; Tue Jan  3 16:59:07 PST 1989
  170. ;;; This file has overgone a major overhaul to be compatible with ispell
  171. ;;; version 2.1.  Most of the functions have been totally rewritten, and
  172. ;;; many user-accessible variables have been added.  The syntax table has
  173. ;;; been removed since it didn't work properly anyway, and a filter is
  174. ;;; used rather than a buffer.  Regular expressions are used based on
  175. ;;; ispell's internal definition of characters (see ispell(4)).
  176. ;;; Some new updates:
  177. ;;; - Updated to version 3.0 to include terse processing.
  178. ;;; - Added a variable for the look command.
  179. ;;; - Fixed a bug in ispell-word when cursor is far away from the word
  180. ;;;   that is to be checked.
  181. ;;; - Ispell places the incorrect word or guess in the minibuffer now.
  182. ;;; - fixed a bug with 'l' option when multiple windows are on the screen.
  183. ;;; - lookup-words just didn't work with the process filter.  Fixed.
  184. ;;; - Rewrote the process filter to make it cleaner and more robust
  185. ;;;   in the event of a continued line not being completed.
  186. ;;; - Made ispell-init-process more robust in handling errors.
  187. ;;; - Fixed bug in continuation location after a region has been modified by
  188. ;;;   correcting a misspelling.
  189. ;;; Mon 17 Sept 1990
  190. ;;; 
  191. ;;; Sebastian Kremer <sk@thp.uni-koeln.de>
  192. ;;; Wed Aug  7 14:02:17 MET DST 1991
  193. ;;; - Ported ispell-complete-word from Ispell 2 to Ispell 3.
  194. ;;; - Added ispell-kill-ispell command.
  195. ;;; - Added ispell-dictionary and ispell-dictionary-alist variables to
  196. ;;;   support other than default language.  See their docstrings and
  197. ;;;   command ispell-change-dictionary.
  198. ;;; - (ispelled it :-)
  199. ;;; - Added ispell-check-tib variable to support the tib bibliography
  200. ;;;   program.
  201. ;;;
  202. ;;;
  203. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  204.  
  205.  
  206.  
  207. ;;; **********************************************************************
  208. ;;; The following variables should be set according to personal preference
  209. ;;; and location of binaries:
  210. ;;; **********************************************************************
  211.  
  212.  
  213. ;;;  ******* THIS FILE IS WRITTEN FOR ISPELL VERSION 3.0
  214.  
  215.  
  216. (provide 'ispell)
  217.  
  218. ;;; Highlighting can slow down display at slow baud and emacs in
  219. ;;; X11 windows cannot take advantage of highlighting (yet).
  220. (defvar ispell-highlight-p nil
  221.   "*When not nil, spelling errors will be highlighted.")
  222.  
  223. (defvar ispell-check-comments nil
  224.   "*When true, the spelling of comments in region is checked.")
  225.  
  226. (defvar ispell-query-replace-choices t
  227.   "*When true and spell checking a region, the correction will be made
  228. throughout the buffer using \\[query-replace].")
  229.  
  230. (defvar ispell-check-tib nil
  231.   "*If non-nil, the spelling of references for the tib(1) bibliography
  232. program is checked.  Else any text between strings matching the regexps
  233. ispell-tib-ref-beginning and ispell-tib-ref-end is ignored, usually what
  234. you want.")
  235.  
  236. (defvar ispell-tib-ref-beginning "[[<]\\."
  237.   "Regexp matching the beginning of a Tib reference.")
  238.  
  239. (defvar ispell-tib-ref-end "\\.[]>]"
  240.   "Regexp matching the end of a Tib reference.")
  241.  
  242. (defvar ispell-keep-choices-win nil
  243.   "*When true, the *Choices* window remains for spelling session.")
  244.  
  245. (defvar ispell-choices-win-default-height 2
  246.   "*The default size of the *Choices*, including status line.
  247.   Must be greater than 1.")
  248.  
  249. (defvar ispell-program-name "ispell"
  250.   "Program invoked by \\[ispell-word] and \\[ispell-region] commands.")
  251.  
  252. (defvar ispell-alternate-dictionary "/usr/dict/words"
  253.   "*Alternate dictionary for spelling help.")
  254.  
  255. (defvar ispell-complete-word-dict ispell-alternate-dictionary
  256.   "*Dictionary used for word completion.")
  257.  
  258. (defvar ispell-grep-command "/usr/bin/egrep"
  259.   "Name of the grep command for search processes.")
  260.  
  261. (defvar ispell-grep-options "-i"
  262.   "Options for ispell-grep-command.
  263. Should probably be \"-i\" or \"-e\".")
  264.  
  265. (defvar ispell-look-p t
  266.   "*Use look.  Should be nil if your UNIX doesn't have this program.
  267. Attempts to automatically reset if look not available")
  268.  
  269. (defvar ispell-look-command "/usr/bin/look"
  270.   "Name of the look command for search processes.
  271. Must contain complete path!")
  272.  
  273. (defvar ispell-look-options "-df"
  274.   "Options for ispell-look-command")
  275.  
  276. (defvar ispell-use-ptys-p nil
  277.   "When t, emacs will use pty's to communicate with ispell.
  278. When nil, emacs will use pipes.")
  279.  
  280. (defvar ispell-following-word nil
  281.   "*If non-nil the \\[ispell-word] command will check the spelling
  282. of the word under or following \(rather than preceding\) the cursor
  283. when called interactively.")
  284.  
  285. (defvar ispell-quietly nil
  286.   "*If non-nil, the \\[ispell-word] command will suppress all
  287. non-corrective messages when called interactively.")
  288.  
  289. (defvar ispell-format-word (function upcase)
  290.   "*The function called to format the ...
  291. The function must take one string argument and return a string.")
  292.  
  293. (defvar ispell-personal-dictionary nil
  294.   "*A string or nil.  If nil, the default directory, ~/.ispell-words is used.")
  295.  
  296. (defvar ispell-tex-major-modes '(plain-TeX-mode plain-tex-mode TeX-mode
  297.                         tex-mode LaTeX-mode latex-mode)
  298.   "The major modes which put ispell into TeX processing mode.")
  299.  
  300.  
  301. (defvar ispell-dictionary nil
  302.   "If non-nil, a dictionary to use instead of the default one.
  303. This is passed to the ispell process using the \"-d\" switch and is
  304. used as key in ispell-dictionary-alist (which see).
  305.  
  306. You should set this variable before your first call to ispell (e.g. in
  307. your .emacs), or use the \\[ispell-change-dictionary] command to
  308. change it, as changing this variable only takes effect in a newly
  309. started ispell process.")
  310.  
  311. (defvar ispell-dictionary-alist        ; sk  9-Aug-1991 18:28
  312.   '((nil                ; default (english.aff) 
  313.      "[A-Za-z]" "[^A-Za-z]" "[---']" nil ("-B") nil)
  314.     ("english"                ; make english explicitly selectable
  315.      "[A-Za-z]" "[^A-Za-z]" "[---']" nil ("-B") nil)
  316.     ("german"                ; german.aff
  317.      "[A-Za-z]" "[^A-Za-z]" "[---'\"]" t ("-C") nil)
  318.     ("swedish"                ;7 bit swedish mode
  319.      "[A-Za-z}{|\\133\\135\\\\]" "[^A-Za-z}{|\\133\\135\\\\]"
  320.      "[---']" nil ("-C") nil)
  321.     ("swedish8"                ;8 bit swedish mode
  322.      "[A-Za-z\345\344\366\305\304\366]"  "[^A-Za-z\345\344\366\305\304\366]"
  323.      "[---']" nil ("-C") "~list")    ; Add `"-T" "list"' to args instead?
  324.     ("french"
  325.      "[A-Za-z]" "[^A-Za-z]" "[---`'\^]" nil nil nil)
  326.     )
  327.   "An alist of dictionaries and their associated parameters.
  328.  
  329. Each element of this list is also a list:
  330.  
  331.     \(DICTIONARY-NAME
  332.         CASECHARS NOT-CASECHARS OTHERCHARS MANY-OTHERCHARS-P
  333.         ISPELL-ARGS EXTENDED-CHARACTER-MODE\)
  334.  
  335. DICTIONARY-NAME is a possible value of variable ispell-dictionary, nil
  336. means the default dictionary.
  337.  
  338. CASECHARS is a regular expression of valid characters that comprise a
  339. word.
  340.  
  341. NOT-CASECHARS is the opposite regexp of CASECHARS.
  342.  
  343. OTHERCHARS is a regular expression of other characters that are valid
  344. in word constructs.  Otherchars cannot be adjacent to each other in a
  345. word, nor can they begin or end a word.  This implies we can't check
  346. \"Stevens'\" as a correct possessive and other correct formations.
  347.  
  348. Hint: regexp syntax requires the hyphen to be declared first here.
  349.  
  350. MANY-OTHERCHARS-P is non-nil if many otherchars are to be allowed in a
  351. word instead of only one.
  352.  
  353. ISPELL-ARGS is a list of additional arguments passed to the ispell
  354. subprocess.
  355.  
  356. EXTENDED-CHARACTER-MODE should be used when dictionaries are used which
  357. have been configured in ispell's parse.y.  (For example, umlauts
  358. can be encoded as \\\"a, a\\\", \"a, ...)  Defaults are ~tex and ~nroff
  359. in english.  This has the same effect as the command-line `-T' option.
  360. The buffer Major Mode controls ispell's parsing in tex or nroff mode,
  361. but the dictionary can control the extended character mode.
  362. Both defaults can be overruled in a buffer-local fashion. See
  363. ispell-parsing-keyword for details on this.
  364.  
  365. Note that the CASECHARS and OTHERCHARS slots of the alist should
  366. contain the same character set as casechars and otherchars in the
  367. language.aff file (e.g., english.aff).")
  368.  
  369.  
  370.  
  371. ;;; **********************************************************************
  372. ;;; The following are used by ispell, and should not be changed.
  373. ;;; **********************************************************************
  374.  
  375.  
  376. (defun ispell-get-casechars ()
  377.   (nth 1 (assoc ispell-dictionary ispell-dictionary-alist)))
  378. (defun ispell-get-not-casechars ()
  379.   (nth 2 (assoc ispell-dictionary ispell-dictionary-alist)))
  380. (defun ispell-get-otherchars ()
  381.   (nth 3 (assoc ispell-dictionary ispell-dictionary-alist)))
  382. (defun ispell-get-many-otherchars-p ()
  383.   (nth 4 (assoc ispell-dictionary ispell-dictionary-alist)))
  384. (defun ispell-get-ispell-args ()
  385.   (nth 5 (assoc ispell-dictionary ispell-dictionary-alist)))
  386. (defun ispell-get-extended-character-mode ()
  387.   (nth 6 (assoc ispell-dictionary ispell-dictionary-alist)))
  388.  
  389.  
  390. (defvar ispell-process nil
  391.   "Holds the process object for 'ispell'")
  392.  
  393. (defvar ispell-pdict-modified-p nil
  394.   "T when the personal dictionary has modifications that need to be written.")
  395.  
  396. (defvar ispell-quit nil
  397.   "Set to t or point when user want to abort ispell session.")
  398.  
  399. (defvar ispell-filter nil
  400.   "Output filter from piped calls to ispell.")
  401.  
  402. (defvar ispell-filter-continue nil
  403.   "Control variable for ispell filter function.")
  404.  
  405. (defvar ispell-process-directory nil
  406.   "The directory where ispell-process was started.")
  407.  
  408. (defvar ispell-saved-selection nil
  409.   "New ver-19 highlighting?")
  410.  
  411. (defvar ispell-query-replace-marker (make-marker)
  412.   "Marker for query-replace processing.")
  413.  
  414.  
  415. ;;; *** Buffer Local Definitions ***
  416.  
  417. (defvar ispell-local-dictionary nil
  418.   "A buffer local variable. If non-nil, a dictionary to be used when running
  419. an ispell-command in this buffer. Setting ispell-local-dictionary to a value
  420. has the same effect as calling \\[ispell-change-dictionary] with that value.
  421. This variable is automatically set when defined in the file with either
  422. ispell-dictionary-keyword or the Local Variable syntax.
  423. If using Local Variable syntax, the dictionary must be a string.")
  424.  
  425. (make-variable-buffer-local 'ispell-local-dictionary)
  426.  
  427. ;; use default directory.  For now it is nil, and unnecessary.
  428. ;; (set-default 'ispell-local-dictionary nil)
  429.  
  430. (defvar ispell-words-keyword "Local IspellWords: "                
  431.   "The keyword for local oddly-spelled words to accept.
  432. The keyword will be followed by any number of local word spellings.
  433. There can be multiple of these keywords in the file.")
  434.  
  435. (defvar ispell-dictionary-keyword "Local IspellDict: "
  436.   "The keyword for local dictionary definitions.
  437. There should be only one dictionary keyword definition per file, and it
  438. should be followed by a correct dictionary name in ispell-dictionary-alist.")
  439.  
  440. (defvar ispell-parsing-keyword "Local IspellParsing: "
  441.   "The keyword for overriding default ispell parsing as determined by
  442. the buffer's major mode and extended-character mode as determined by the
  443. default dictionary.
  444.   The above keyword string should be followed by `latex-mode' or
  445. `nroff-mode' to put the current buffer into the desired parsing mode.
  446.   Extended character mode can be changed for this buffer by placing
  447. a `~' followed by an extended-character mode -- such as `~tex'.")
  448.  
  449. (defvar ispell-buffer-local-name nil
  450.   "Contains the buffer name if local definitions were used.")
  451.  
  452.  
  453. ;;; **********************************************************************
  454. ;;; **********************************************************************
  455.  
  456.  
  457.  
  458. (defun ispell-word (&optional following quietly)
  459.   "Check spelling of word under or before the cursor.
  460. If word not found in dictionary, display possible corrections in a window
  461. and let user select.
  462.   If optional argument FOLLOWING is non-nil or if ispell-following-word
  463. is non-nil when called interactively, then the following word
  464. \(rather than preceding\) will be checked when the cursor is not over a word.
  465.   When the optional argument QUIETLY is non-nil or ispell-quietly is non-nil
  466. when called interactively, non-corrective messages are suppressed.
  467.  
  468.   Word syntax described by ispell-dictionary-alist (which see)."
  469.   (interactive)
  470.   (if (interactive-p)
  471.       (setq following ispell-following-word
  472.         quietly ispell-quietly))
  473.   (ispell-buffer-local-dict)        ; use the correct dictionary
  474.   (let ((cursor-location (point))    ; retain cursor location
  475.     ispell-keep-choices-win        ; override global to force creation
  476.     (word (ispell-get-word following))
  477.     start end poss replace)
  478.     ;; destructure return word info list.
  479.     (setq start (car (cdr word))
  480.       end (car (cdr (cdr word)))
  481.       word (car word))
  482.  
  483.     ;; now check spelling of word.
  484.     (or quietly
  485.     (message "Checking spelling of %s..."
  486.          (funcall ispell-format-word word)))
  487.     (ispell-init-process)        ; erases ispell output buffer
  488.     (process-send-string ispell-process "%\n") ;put in verbose mode
  489.     (process-send-string ispell-process (concat "^" word "\n"))
  490.     ;; wait until ispell has processed word
  491.     (while (progn
  492.          (accept-process-output ispell-process)
  493.          (not (string= "" (car ispell-filter)))))
  494.     ;;(process-send-string ispell-process "!\n") ;back to terse mode.
  495.     (setq ispell-filter (cdr ispell-filter))
  496.     (if (listp ispell-filter)
  497.     (setq poss (ispell-parse-output (car ispell-filter))))
  498.     (cond ((eq poss t)
  499.        (or quietly
  500.            (message "%s is correct." (funcall ispell-format-word word))))
  501.       ((stringp poss)
  502.        (or quietly
  503.            (message "%s is correct because of root %s"
  504.             (funcall ispell-format-word word)
  505.             (funcall ispell-format-word poss))))
  506.       ((null poss) (message "Error in ispell process"))
  507.       (t                ; prompt for correct word.
  508.        (unwind-protect
  509.            (progn
  510.          (if ispell-highlight-p
  511.              (highlight-spelling-error start end t)) ; highlight word
  512.          (setq replace (ispell-choose (car (cdr (cdr poss)))
  513.                           (car (cdr (cdr (cdr poss))))
  514.                           (car poss))))
  515.          ;; protected
  516.          (if ispell-highlight-p    ; clear highlight
  517.          (highlight-spelling-error start end)))
  518.        (cond ((equal 0 replace)
  519.           (ispell-add-per-file-word-list (car poss)))
  520.          (replace
  521.           (delete-region start end)
  522.           (setq word (if (atom replace) replace (car replace))
  523.             cursor-location (+ (- (length word) (- end start))
  524.                        cursor-location))
  525.           (insert-string word)
  526.           (if (not (atom replace)) ; recheck spelling of replacement
  527.               (progn
  528.             (goto-char cursor-location)
  529.             (ispell-word following quietly)))))
  530.        (if (get-buffer "*Choices*")
  531.            (kill-buffer "*Choices*"))))
  532.     (goto-char cursor-location)        ; return to original location
  533.     (ispell-pdict-save)
  534.     (if ispell-quit (setq ispell-quit nil))))
  535.  
  536.  
  537. (defun ispell-get-word (following &optional extra-otherchars)
  538.   "Return the word for spell-checking according to ispell syntax.
  539.   If optional argument FOLLOWING is non-nil or if ispell-following-word
  540. is non-nil when called interactively, then the following word
  541. \(rather than preceeding\) will be checked when the cursor is not over a word.
  542.   Optional second argument contains otherchars that can be included in word
  543. many times.
  544.  
  545.   Word syntax described by ispell-dictionary-alist (which see)."
  546.   (let* ((ispell-casechars (ispell-get-casechars))
  547.      (ispell-not-casechars (ispell-get-not-casechars))
  548.      (ispell-otherchars (ispell-get-otherchars))
  549.      (ispell-many-otherchars-p (ispell-get-many-otherchars-p))
  550.      (word-regexp (concat ispell-casechars
  551.                   "+\\("
  552.                   ispell-otherchars
  553.                   "?"
  554.                   (if extra-otherchars
  555.                   (concat extra-otherchars "?"))
  556.                   ispell-casechars
  557.                   "+\\)"
  558.                   (if (or ispell-many-otherchars-p
  559.                       extra-otherchars)
  560.                   "*" "?")))
  561.      did-it-once
  562.      start end word)
  563.     ;; find the word
  564.     (if (not (looking-at ispell-casechars))
  565.     (if following
  566.         (re-search-forward ispell-casechars (point-max) t)
  567.       (re-search-backward ispell-casechars (point-min) t)))
  568.     ;; move to front of word
  569.     (re-search-backward ispell-not-casechars (point-min) 'start)
  570.     (while (and (or (looking-at ispell-otherchars)
  571.             (and extra-otherchars (looking-at extra-otherchars)))
  572.         (not (bobp))
  573.         (or (not did-it-once)
  574.             ispell-many-otherchars-p))
  575.       (if (and extra-otherchars (looking-at extra-otherchars))
  576.       (progn
  577.         (backward-char 1)
  578.         (if (looking-at ispell-casechars)
  579.         (re-search-backward ispell-not-casechars (point-min) t)))
  580.     (setq did-it-once t)
  581.     (backward-char 1)
  582.     (if (looking-at ispell-casechars)
  583.         (re-search-backward ispell-not-casechars (point-min) t)
  584.       (backward-char -1))))
  585.     ;; Now mark the word and save to string.
  586.     (or (re-search-forward word-regexp (point-max) t)
  587.     (error "No word found to check!"))
  588.     (setq start (match-beginning 0)
  589.       end (point)
  590.       word (buffer-substring start end))
  591.     (list word start end)))
  592.  
  593.  
  594. ;;; Global ispell-pdict-modified-p is set by ispell-choose and
  595. ;;; tracks changes in the dictionary.  The global may either be
  596. ;;; a value or a list, whose value is the state of whether the
  597. ;;; dictionary needs to be saved.
  598.  
  599. (defun ispell-pdict-save (&optional force-save)
  600.   "Check to see if the personal dictionary has been modified.
  601.   If so, ask if it needs to be saved."
  602.   (interactive)
  603.   (if (interactive-p) (setq force-save t))
  604.   (if (and ispell-pdict-modified-p (listp ispell-pdict-modified-p))
  605.       (setq ispell-pdict-modified-p (car ispell-pdict-modified-p)))
  606.   (if (or ispell-pdict-modified-p force-save)
  607.       (if (y-or-n-p "Personal dictionary modified.  Save? ")
  608.       (process-send-string ispell-process "#\n")))
  609.   ;; unassert variable, even if not saved to avoid questioning.
  610.   (setq ispell-pdict-modified-p nil))
  611.  
  612.  
  613. (defun ispell-choose (miss guess word)
  614.   "Display possible corrections from list MISS.
  615. GUESS lists possibly valid affix construction of WORD.
  616. Returns nil to keep word.
  617.     0 to insert locally into buffer-local dictionary.
  618.         string for new chosen word.
  619.         list for new replacement word (will be rechecked).
  620.       Optional second argument means replace misspelling in
  621.       the rest of the region.
  622. Global ispell-pdict-modified-p becomes a list where the only value
  623. indicates whether the dictionary has been modified when option a or i is
  624. used."
  625.   (unwind-protect
  626.   (save-window-excursion
  627.   (let ((count ?0)
  628.     (line 2)
  629.     (max-lines (- (window-height) 4)) ; assure 4 context lines.
  630.     (choices miss)
  631.     (window-min-height (min window-min-height
  632.                 ispell-choices-win-default-height))
  633.     (command-characters '( ?  ?i ?a ?A ?r ?R ?? ?x ?X ?q ?l ))
  634.     (skipped 0)
  635.     char num result)
  636.     (save-excursion
  637.     (if ispell-keep-choices-win
  638.     (select-window (previous-window))
  639.       (set-buffer (get-buffer-create "*Choices*"))
  640.       (setq mode-line-format "--  %b  --"))
  641.     (if (equal (get-buffer "*Choices*") (current-buffer))
  642.     (erase-buffer)
  643.       (error "Bogus, dude! I should be in the *Choices* buffer, but I'm not!"))
  644.     (if guess
  645.     (progn
  646.       (insert
  647.        "\tAffix rules generate and capitalize this word as shown below:\n")
  648.       (while guess
  649.         (if (> (+ 4 (current-column) (length (car guess)))
  650.            (window-width))
  651.         (progn
  652.           (insert "\n")
  653.           (setq line (1+ line))))
  654.         (insert (car guess) "    ")
  655.         (setq guess (cdr guess)))
  656.       (insert "\nUse option \"i\" if this is a correct composition from the derivative root.\n\n")
  657.       (setq line (+ line 4))))
  658.     (while (and choices
  659.         (< (if (> (+ 7 (current-column) (length (car choices))
  660.                  (if (> count ?~) 3 0))
  661.               (window-width))
  662.                (progn
  663.              (insert "\n")
  664.              (setq line (1+ line)))
  665.              line)
  666.            max-lines))
  667.       ;; not so good if there are over 20 or 30 options, but then, if
  668.       ;; there are that many you don't want to have to scan them all anyway...
  669.       (while (memq count command-characters) ; skip command characters.
  670.     (setq count (1+ count)
  671.           skipped (1+ skipped)))
  672.       (insert "(" count ") " (car choices) "  ")
  673.       (setq choices (cdr choices)
  674.         count (1+ count)))
  675.     (setq count (- count ?0 skipped)))
  676.  
  677.     (if ispell-keep-choices-win
  678.     (if (> line ispell-keep-choices-win)
  679.         (progn
  680.           (switch-to-buffer "*Choices*")
  681.           (select-window (next-window))
  682.           (save-excursion
  683.         (let ((cur-point (point)))
  684.           (move-to-window-line (- line ispell-keep-choices-win))
  685.           (if (<= (point) cur-point)
  686.               (set-window-start (selected-window) (point)))))
  687.           (select-window (previous-window))
  688.           (enlarge-window (- line ispell-keep-choices-win))
  689.           (goto-char (point-min))))
  690.       (overlay-window (max line ispell-choices-win-default-height)))
  691.     (switch-to-buffer "*Choices*")
  692.     (goto-char (point-min))
  693.     (select-window (next-window))
  694.     (while
  695.     (eq
  696.      t
  697.      (setq
  698.       result
  699.       (progn
  700.         (message "C-h or ? for more options; SPC to leave unchanged, Character to replace word")
  701.         (setq char (read-char)
  702.           skipped 0)
  703.         ;; Adjust num to array offset skipping command characters.
  704.         (let ((com-chars command-characters))
  705.           (while com-chars
  706.         (if (and (> (car com-chars) ?0) (< (car com-chars) char))
  707.             (setq skipped (1+ skipped)))
  708.         (setq com-chars (cdr com-chars)))
  709.           (setq num (- char ?0 skipped)))
  710.  
  711.         (cond
  712.          ((= char ? ) nil)        ; accept word this time only
  713.          ((= char ?i)        ; accept and insert word into pers dict
  714.           (process-send-string ispell-process (concat "*" word "\n"))
  715.           (setq ispell-pdict-modified-p '(t)) ; dictionary was modified!
  716.           nil)
  717.          ((or (= char ?a) (= char ?A)) ; accept word, don't insert in dict
  718.           (process-send-string ispell-process (concat "@" word "\n"))
  719.           (if (null ispell-pdict-modified-p)
  720.           (setq ispell-pdict-modified-p
  721.             (list ispell-pdict-modified-p)))
  722.           (if (= char ?A) 0))    ; return 0 for ispell-add buffer-local
  723.          ((or (= char ?r) (= char ?R)) ; type in replacement
  724.           (if (or (= char ?R) ispell-query-replace-choices)
  725.           (list (read-string "Query-replacement for: " word) t)
  726.         (cons (read-string "Replacement for: " word) nil)))
  727.          ((or (= char ??) (= char help-char) (= char ?\C-h))
  728.           (ispell-choose-help)
  729.           t)
  730.          ((= char ?x)        ; quit.
  731.           (setq ispell-quit t) nil)
  732.          ((= char ?X)        ; quit but stay at this point.
  733.           (setq ispell-quit (point)) nil)
  734.          ((= char ?q)
  735.           (if (y-or-n-p "Really quit ignoring changes? ")
  736.           (progn
  737.             (ispell-kill-ispell t) ; terminate process.
  738.             (setq ispell-quit t
  739.               ispell-pdict-modified-p nil))
  740.         t))            ; continue if they don't quit.
  741.          ((= char ?l)
  742.           (let ((new-word (read-string "Lookup string ('*' is wildcard): "
  743.                        word))
  744.             (new-line 2))
  745.         (if new-word
  746.             (progn
  747.               (save-excursion
  748.             (set-buffer (get-buffer-create "*Choices*"))
  749.             (erase-buffer)
  750.             (setq count ?0
  751.                   skipped 0
  752.                   mode-line-format "--  %b  --"
  753.                   miss (lookup-words new-word)
  754.                   choices miss)
  755.             (while (and choices ; adjust choices window.
  756.                     (< (if (> (+ 7 (current-column)
  757.                          (length (car choices))
  758.                          (if (> count ?~) 3 0))
  759.                           (window-width))
  760.                        (progn
  761.                          (insert "\n")
  762.                          (setq new-line (1+ new-line)))
  763.                      new-line)
  764.                        max-lines))
  765.               (while (memq count command-characters)
  766.                 (setq count (1+ count)
  767.                   skipped (1+ skipped)))
  768.               (insert "(" count ") " (car choices) "  ")
  769.               (setq choices (cdr choices)
  770.                 count (1+ count)))
  771.             (setq count (- count ?0 skipped)))
  772.               (select-window (previous-window))
  773.               (if (/= new-line line)
  774.               (progn
  775.                 (if (> new-line line)
  776.                 (enlarge-window (- new-line line))
  777.                   (shrink-window (- line new-line)))
  778.                 (setq line new-line)))
  779.               (select-window (next-window)))))
  780.           t)            ; reselect from new choices
  781.          ((and (>= num 0) (< num count))
  782.           (if ispell-query-replace-choices ; Query replace when flag set.
  783.           (list (nth num miss) 'query-replace)
  784.         (nth num miss)))
  785.          ((= char ?\C-l)
  786.           (redraw-display) t)
  787.          ((= char ?\C-r)
  788.           (save-window-excursion (recursive-edit)) t)
  789.          ((= char ?\C-z)
  790.           (suspend-emacs) t)
  791.          (t (ding) t))))))
  792.     result))
  793.   (if (not ispell-keep-choices-win) (bury-buffer "*Choices*"))))
  794.  
  795.  
  796. (defun ispell-choose-help ()
  797.   (let ((help-1 "[r/R]eplace word; [a/A]ccept for this session; [i]nsert into private dictionary")
  798.     (help-2 "[l]ook a word up in alternate dictionary;  e[x/X]it;  [q]uit session."))
  799.     (if (and (boundp 'epoch::version)
  800.          (equal epoch::version "Epoch 3.1"))
  801.     ;; Enlarging the minibuffer crashes Epoch 3.1
  802.     (with-output-to-temp-buffer "*Ispell Help*"
  803.       (princ help-1)
  804.       (princ "\n")
  805.       (princ help-2))
  806.       (save-window-excursion
  807.     (select-window (minibuffer-window))
  808.     (message help-2)
  809.     (enlarge-window 1)
  810.     (message help-1)
  811.     (sit-for 5)
  812.     (erase-buffer)))))
  813.  
  814.  
  815. (defun lookup-words (word &optional lookup-dict)
  816.   "Look up word in word-list dictionary.
  817. A '*' is used for wild cards.  If no wild cards, 'look' is used if it exists.
  818.  Otherwise the variable ispell-grep-command contains the command used to
  819.  search for the words (usually egrep).
  820. Optional second argument contains the dictionary to use, the default is
  821.  ispell-alternate-dictionary."
  822.   ;; We don't use the filter for this function, rather the result is written
  823.   ;; into a buffer.  Hence there is no need to save the filter values.
  824.   (if (null lookup-dict)
  825.       (setq lookup-dict ispell-alternate-dictionary))
  826.   (let ((process-connection-type ispell-use-ptys-p)
  827.     (do-look (and ispell-look-p    ; Only use look for an exact match.
  828.               (not (string-match "\\*" word)) 
  829.               (setq ispell-look-p
  830.                 (file-exists-p ispell-look-command))))
  831.     (ispell-grep-buffer (get-buffer-create "*Ispell-Temp*")) ; result buf
  832.     retval results loc)
  833.     (unwind-protect
  834.     (save-window-excursion
  835.       (message "Starting \"%s\" process..." (if do-look "look" "grep"))
  836.       (if (not do-look)            ; Format correctly for grep search.
  837.       (let ((start 0)
  838.         new-word end)
  839.         (while (progn        ; change "*"'s to ".*"'s.
  840.              (if (setq end (string-match "\\*" word start))
  841.              (setq new-word (concat new-word
  842.                         (substring word start end)
  843.                         ".*")
  844.                    start (1+ end))
  845.                (setq new-word (concat new-word (substring word start)))
  846.                nil)))
  847.         (setq word (concat "^" new-word "$"))))
  848.  
  849.       (set-buffer ispell-grep-buffer)
  850.       (setq retval
  851.         (call-process (if do-look ispell-look-command ispell-grep-command)
  852.               nil t nil
  853.               (if do-look ispell-look-options ispell-grep-options)
  854.               word lookup-dict))
  855.       (if (and retval (not (eq retval 0)))
  856.       (setq results (cons (concat "error: exited with signal " retval)
  857.                   results))
  858.     ;; Collect words into `results' in FIFO order
  859.     (goto-char (point-max))
  860.     ;; assure we've ended with \n
  861.     (or (bobp) (= (preceding-char) ?\n) (insert ?\n))
  862.     (while (not (bobp))
  863.       (setq loc (point))
  864.       (forward-line -1)
  865.       (setq results (cons (buffer-substring (point) (1- loc)) results)))))
  866.     ;; protected
  867.     (kill-buffer ispell-grep-buffer)
  868.     (if (and results (string-match ".+: " (car results)))
  869.     (progn                ; Error occured.  Display error message
  870.       (message "%s" (car results))
  871.       (setq results nil)
  872.       (sit-for 3))))
  873.     results))
  874.  
  875.  
  876. ;;; "ispell-filter" is a list of output lines from the generating function.
  877. ;;;   Each full line (ending with \n) is a separate item on the list.
  878. ;;; "output" can contain multiple lines, part of a line, or both.
  879. ;;; "start" and "end" are used to keep bounds on lines when "output" contains
  880. ;;;   multiple lines.
  881. ;;; "ispell-filter-continue" is true when we have received only part of a
  882. ;;;   line as output from a generating function ("output" did not end with \n)
  883. ;;; NOTE THAT THIS FUNCTION WILL FAIL IF THE PROCESS OUTPUT DOESNT END WITH \n!
  884. ;;;   This is the case when a process dies or fails. The default behavior
  885. ;;;   in this case treats the next input received as fresh input.
  886.  
  887. (defun ispell-filter (process output)
  888.   "Output filter function for ispell, grep, and look."
  889.   (let ((start 0)
  890.     (continue t)
  891.     end)
  892.     (while continue
  893.       (setq end (string-match "\n" output start)) ; get text up to the newline.
  894.       ;; If we get out of sync and ispell-filter-continue is asserted when we
  895.       ;; are not continuing, treat the next item as a separate list.  When
  896.       ;; ispell-filter-continue is asserted, ispell-filter *should* always be a
  897.       ;; list!
  898.  
  899.       ;; Continue with same line (item)?
  900.       (if (and ispell-filter-continue ispell-filter (listp ispell-filter))
  901.       ;; Yes.  Add it to the prev item
  902.       (setcar ispell-filter
  903.           (concat (car ispell-filter) (substring output start end)))
  904.     ;; No. This is a new line and item.
  905.     (setq ispell-filter
  906.           (cons (substring output start end) ispell-filter)))
  907.       (if (null end)
  908.       ;; We've completed reading the output, but didn't finish the line.
  909.       (setq ispell-filter-continue t continue nil)
  910.     ;; skip over newline, this line complete.
  911.     (setq ispell-filter-continue nil end (1+ end))
  912.     (if (= end (length output))    ; No more lines in output
  913.         (setq continue nil)        ;  so we can exit the filter.
  914.       (setq start end))))))        ; else move start to next line of input
  915.  
  916.  
  917. ;;; For versions less than 19 this function destroys the mark location
  918. ;;; if it is in the word being highlighted.
  919.  
  920. (defun highlight-spelling-error (start end &optional highlight)
  921.   "Highlight a word by toggling inverse-video.
  922.   highlights word from START to END.
  923.   When the optional third arg HIGHLIGHT is set, the word is drawn in inverse
  924.   video, otherwise the word is drawn in normal video mode."
  925.   (if (string-match "^19\\." emacs-version)
  926.       (if (string-match "Lucid" emacs-version)
  927.       (highlight-spelling-error-v19-Lucid start end highlight)
  928.     (highlight-spelling-error-v19 start end highlight))
  929.     (let ((modified (buffer-modified-p)) ; don't allow this fn to modify buffer
  930.       (text (buffer-substring start end)) ; Save highlight region
  931.       (inhibit-quit t)        ; inhibit interrupt processing here.
  932.       (buffer-undo-list nil))    ; don't clutter the undo list.
  933.       (delete-region start end)
  934.       (insert-char ?  (- end start))    ; mimimize amount of redisplay
  935.       (sit-for 0)            ; update display
  936.       (if highlight (setq inverse-video (not inverse-video))) ; toggle video
  937.       (delete-region start end)        ; delete whitespace
  938.       (insert text)            ; insert text in inverse video.
  939.       (sit-for 0)            ; update display showing inverse video.
  940.       (if highlight (setq inverse-video (not inverse-video))) ; toggle video
  941.       (set-buffer-modified-p modified)))) ; don't modify if flag not set.
  942.  
  943.  
  944. ;;; debug debug debug debug
  945. ;;; The next two functions are not complete!
  946.  
  947. (defun highlight-spelling-error-v19-Lucid (start end &optional highlight)
  948.   (if highlight
  949.       (isearch-highlight start end)
  950.     (isearch-dehighlight t))
  951.   (sit-for 0))
  952.  
  953. (defun highlight-spelling-error-v19 (start end &optional highlight)
  954.   (if highlight
  955.       (setq ispell-saved-selection (cons selection-begin selection-end)
  956.         selection-begin (set-marker (make-marker) start)
  957.         selection-end (set-marker (make-marker) end))
  958.     (setq selection-begin (car ispell-saved-selection)
  959.       selection-end (cdr ispell-saved-selection)
  960.       ispell-saved-selection nil))
  961.   (sit-for 0))
  962.  
  963.  
  964. (defun overlay-window (height)
  965.   "Create a (usually small) window with HEIGHT lines and avoid recentering."
  966.   (save-excursion
  967.     (let ((oldot (save-excursion (forward-line -1) (point)))
  968.       (top (save-excursion (move-to-window-line height) (point))))
  969.       (if (< oldot top) (setq top oldot))
  970.       (split-window-vertically height)
  971.       (set-window-start (next-window) top))))
  972.  
  973.  
  974. (defun ispell-parse-output (output)
  975.   "Parse the OUTPUT string of 'ispell' and return:
  976. 1: T for an exact match.
  977. 2: A string containing the root word for a match via suffix removal.
  978. 3: A list of possible correct spellings of the format:
  979.    '(\"original-word\" offset miss-list guess-list)
  980.    original-word is a string of the possibly misspelled word.
  981.    offset is an integer giving the line offset of the word.
  982.    miss-list and guess-list are possibly null lists of guesses and misses."
  983.   (cond
  984.    ((string= output "") t)        ; for startup with pipes...
  985.    ((string= output "*") t)        ; exact match
  986.    ((string= (substring output 0 1) "+") ; found cuz of root word
  987.     (substring output 2))        ; return root word
  988.    (t                    ; need to process &, ?, and #'s
  989.     (let ((type (substring output 0 1))    ; &, ?, or #
  990.       (original-word (substring output 2 (string-match " " output 2)))
  991.       (cur-count 0)            ; contains number of misses + guesses
  992.       count miss-list guess-list offset)
  993.       (setq output (substring output (match-end 0))) ; skip over misspelling
  994.       (if (string= type "#")
  995.       (setq count 0)        ; no misses for type #
  996.     (setq count (string-to-int output) ; get number of misses.
  997.           output (substring output (1+ (string-match " " output 1)))))
  998.       (setq offset (string-to-int output))
  999.       (if (string= type "#")        ; No miss or guess list.
  1000.       (setq output nil)
  1001.     (setq output (substring output (1+ (string-match " " output 1)))))
  1002.       (while output
  1003.     (let ((end (string-match ", \\|\\($\\)" output))) ; end of miss/guess.
  1004.       (setq cur-count (1+ cur-count))
  1005.       (if (> cur-count count)
  1006.           (setq guess-list (cons (substring output 0 end) guess-list))
  1007.         (setq miss-list (cons (substring output 0 end) miss-list)))
  1008.       (if (match-end 1)        ; True only when at end of line.
  1009.           (setq output nil)        ; no more misses or guesses
  1010.         (setq output (substring output (+ end 2))))))
  1011.       (list original-word offset miss-list guess-list)))))
  1012.  
  1013.  
  1014. (defun ispell-init-process ()
  1015.   "Check status of 'ispell' process and start if necessary."
  1016.   (if (and ispell-process
  1017.        (eq (process-status ispell-process) 'run)
  1018.        (equal ispell-process-directory default-directory))
  1019.       (setq ispell-filter nil ispell-filter-continue nil)
  1020.     ;; may need to restart to select new dictionary.
  1021.     (ispell-kill-ispell t)
  1022.     (message "Starting new ispell process...")
  1023.     (sit-for 0)
  1024.     (setq ispell-process
  1025.       (let ((process-connection-type ispell-use-ptys-p))
  1026.         (apply 'start-process
  1027.            "ispell" nil ispell-program-name
  1028.            "-a"            ; accept single input lines
  1029.            "-m"            ; make root/affix combos not in dict
  1030.            (let ((args (ispell-get-ispell-args)))
  1031.              (if ispell-dictionary ; use specified dictionary
  1032.              (setq args
  1033.                    (append (list "-d" ispell-dictionary)
  1034.                        args)))
  1035.              (if ispell-personal-dictionary ; use specified pers dict
  1036.              (setq args
  1037.                    (append (list "-p" ispell-personal-dictionary)
  1038.                        args)))
  1039.              args)))
  1040.       ispell-filter nil
  1041.       ispell-filter-continue nil
  1042.       ispell-process-directory default-directory)
  1043.     (set-process-filter ispell-process 'ispell-filter)
  1044.     (while (progn            ; Get version ID line
  1045.          (accept-process-output ispell-process)
  1046.          (not (eq (process-status ispell-process) 'run))))
  1047.     (setq ispell-filter nil)        ; Discard version ID line
  1048.     (let ((extended-char-mode (ispell-get-extended-character-mode)))
  1049.       (if extended-char-mode
  1050.       (process-send-string (concat extended-char-mode "\n"))))
  1051.     (process-kill-without-query ispell-process)))
  1052.  
  1053.  
  1054. (defun ispell-kill-ispell (&optional no-error)
  1055.   "Kill current ispell process (so that you may start a fresh one)."
  1056.   ;; With NO-ERROR, just return non-nil if there was no ispell running.
  1057.   (interactive)
  1058.   (if (not (and ispell-process
  1059.         (eq (process-status ispell-process) 'run)))
  1060.       (or no-error
  1061.       (error "There is no ispell process running!"))
  1062.     (kill-process ispell-process)
  1063.     (setq ispell-process nil)
  1064.     (message "Killed ispell process.")
  1065.     nil))
  1066.  
  1067.  
  1068. (defun ispell-change-dictionary (dict)
  1069.   "Change ispell-dictionary (q.v.) and kill old ispell process.
  1070. A new one will be started as soon as necessary.
  1071.  
  1072. By just answering RET you can find out what the current dictionary is."
  1073.   (interactive
  1074.    (list (completing-read "Use new ispell dictionary (type SPC to complete): "
  1075.               ispell-dictionary-alist nil t)))
  1076.   ;; Like info.el, we also rely on completing-read's bug of returning ""
  1077.   ;; even if this is not in the table:
  1078.   (if (or (equal dict "") (equal dict ispell-dictionary))
  1079.       (message "(No change, using %s dictionary)" ispell-dictionary)
  1080.     (if (assoc dict ispell-dictionary-alist)
  1081.     (setq ispell-dictionary dict)
  1082.       (error "Illegal dictionary: %s" dict))
  1083.     (ispell-kill-ispell t)
  1084.     (message "(Next ispell command will use %s dictionary)"
  1085.          (or dict "default"))))
  1086.  
  1087.  
  1088.  
  1089. ;;; Spelling of comments are checked when ispell-check-comments is non-nil.
  1090.  
  1091. (defun ispell-region (reg-start reg-end)
  1092.   "Interactively check a region for spelling errors."
  1093.   (interactive "*r")
  1094.   (ispell-accept-buffer-local-defs)    ; set up dictionary, local words, etc.
  1095.   (unwind-protect
  1096.   (save-excursion
  1097.   (message "Spelling %s..."
  1098.        (if (and (= reg-start (point-min)) (= reg-end (point-max)))
  1099.            (buffer-name) "region"))
  1100.   (sit-for 0)
  1101.   ;; must be top level now, not inside ispell-choose for keeping window around.
  1102.   (save-window-excursion
  1103.   (if ispell-keep-choices-win
  1104.       (let ((window-min-height ispell-choices-win-default-height))
  1105.     ;; This keeps the default window size when choices window saved.
  1106.     (setq ispell-keep-choices-win ispell-choices-win-default-height)
  1107.     (overlay-window ispell-choices-win-default-height)
  1108.     (switch-to-buffer (get-buffer-create "*Choices*"))
  1109.     (setq mode-line-format "--  %b  --")
  1110.     (erase-buffer)
  1111.     (select-window (next-window))
  1112.     (sit-for 0)))
  1113.   (goto-char reg-start)
  1114.   (while (and (not ispell-quit) (< (point) reg-end))
  1115.     (let ((start (point))
  1116.       (offset-change 0)
  1117.       (end (save-excursion (end-of-line) (min (point) reg-end)))
  1118.       (ispell-casechars (ispell-get-casechars))
  1119.       string)
  1120.       (cond                ; LOOK AT THIS LINE AND SKIP OR PROCESS
  1121.        ((eolp)                ; END OF LINE, just go to next line.
  1122.     (forward-char 1))
  1123.        ((and (null ispell-check-comments) ; SKIPING COMMENTS
  1124.          comment-start        ; skip comments that start on the line.
  1125.          (search-forward comment-start end t)) ; a comment is on this line.
  1126.     (if (= (- (point) start) (length comment-start))
  1127.         ;; comment starts the line.  We can skip the entire line or region
  1128.         (if (string= "" comment-end) ; skip to next line over comment
  1129.         (beginning-of-line 2)
  1130.           (search-forward comment-end reg-end 'limit)) ; jmp to comment end
  1131.       ;; Comment starts later on line.  Check for spelling before comment.
  1132.       (let ((limit (- (point) (length comment-start))))
  1133.         (goto-char (1- limit))
  1134.         (if (looking-at "\\\\")    ; "quoted" comment, don't skip
  1135.         ;; quoted comment.  Skip over comment-start and continue.
  1136.         (if (= start (1- limit))
  1137.             (setq limit (+ limit (length comment-start)))
  1138.           (setq limit (1- limit))))
  1139.         (goto-char start)
  1140.         ;; Only check if there are "casechars" or math chars before comment
  1141.         (if (or (re-search-forward ispell-casechars limit t)
  1142.             (re-search-forward "[][()$]" limit t))
  1143.         (setq string (concat "^" (buffer-substring start limit) "\n")))
  1144.         (goto-char limit))))
  1145.        ((and (null ispell-check-tib)    ; SKIP TIB REFERENCES!
  1146.          (re-search-forward ispell-tib-ref-beginning end t))
  1147.     (if (= (- (point) 2) start)    ; tib ref is 2 chars.
  1148.         ;; Skip to end of tib ref, not necessarily on this line.
  1149.         (re-search-forward ispell-tib-ref-end reg-end 'move)
  1150.       ;; tib ref starts later on line.  Check spelling before tib.
  1151.       (let ((limit (- (point) 2)))
  1152.         (goto-char start)
  1153.         (if (or (re-search-forward ispell-casechars limit t)
  1154.             (re-search-forward "[][()$]" limit t))
  1155.         (setq string (concat "^" (buffer-substring start limit) "\n")))
  1156.         (goto-char limit))))
  1157.        ((looking-at "[---#@*+!%~^]")    ; SKIP SPECIAL ISPELL CHARACTERS
  1158.     (forward-char 1))
  1159.        ((or (re-search-forward ispell-casechars end t) ; TEXT EXISTS...
  1160.         (re-search-forward "[][()$]" end t)) ; or MATH COMMANDS...
  1161.     (setq string (concat "^" (buffer-substring start end) "\n"))
  1162.     (goto-char end))
  1163.        (t (beginning-of-line 2)))    ; EMPTY LINE, skip it.
  1164.  
  1165.       (setq end (point))        ; "end" tracks end of region to check.
  1166.  
  1167.       (if string            ; there is something to spell!
  1168.       (let (poss)
  1169.         ;; send string to spell process and get input.
  1170.         (process-send-string ispell-process string)
  1171.         (while (progn
  1172.              (accept-process-output ispell-process)
  1173.              ;; Last item of output contains a blank line.
  1174.              (not (string= "" (car ispell-filter)))))
  1175.         ;; parse all inputs from the stream one word at a time.
  1176.         ;; Place in FIFO order and remove the blank item.
  1177.         (setq ispell-filter (nreverse (cdr ispell-filter)))
  1178.         (while (and (not ispell-quit) ispell-filter)
  1179.           (setq poss (ispell-parse-output (car ispell-filter)))
  1180.           (if (listp poss)        ; spelling error occurred.
  1181.           (let* ((word-start (+ start offset-change (car (cdr poss))))
  1182.              (word-end (+ word-start (length (car poss))))
  1183.              replace)
  1184.             (goto-char word-start)
  1185.             (if (/= word-end (progn
  1186.                        (search-forward (car poss) word-end t)
  1187.                        (point)))
  1188.             ;; This usually occurs due to filter pipe problems
  1189.             (error "***ispell misalignment: word \"%s\" point %d; please retry."
  1190.                    (car poss) word-start))
  1191.             (unwind-protect
  1192.             (progn
  1193.               (if ispell-highlight-p
  1194.               (highlight-spelling-error word-start word-end t)
  1195.             (sit-for 0))    ;DBH 15-Jul-1993
  1196.               (setq replace (ispell-choose (car (cdr (cdr poss)))
  1197.                            (car (cdr (cdr (cdr poss))))
  1198.                            (car poss))))
  1199.             ;; protected
  1200.             (if ispell-highlight-p
  1201.             (highlight-spelling-error word-start word-end)))
  1202.             (cond
  1203.              ((and replace (listp replace))
  1204.               ;; REPLACEMENT WORD entered.  Recheck line starting with
  1205.               ;; the replacement word.
  1206.               (setq ispell-filter nil
  1207.                 string (buffer-substring word-start word-end))
  1208.               (let ((change (- (length (car replace)) ; adjust
  1209.                        (length (car poss))))) ;  regions
  1210.             (setq reg-end (+ reg-end change)
  1211.                   offset-change (+ offset-change change)))
  1212.               (delete-region word-start word-end)
  1213.               (insert (car replace))
  1214.               ;; I only need to recheck typed-in replacements.
  1215.               (if (not (eq 'query-replace (car (cdr replace))))
  1216.               (backward-char (length (car replace))))
  1217.               (setq end (point)) ; reposition in region to recheck
  1218.               ;; when second arg exists, query-replace, saving regions
  1219.               (if (car (cdr replace))
  1220.               (unwind-protect
  1221.               (progn
  1222.                 (set-marker ispell-query-replace-marker reg-end)
  1223.                 ;; Assume case-replace & case-fold-search correct?
  1224.                 (query-replace string (car replace)))
  1225.               ;; protected
  1226.               (setq reg-end (marker-position
  1227.                      ispell-query-replace-marker))
  1228.               (set-marker ispell-query-replace-marker nil))))
  1229.              ((or (null replace) (equal 0 replace)) ; ACCEPT/INSERT
  1230.               (if (equal 0 replace) ; BUFFER-LOCAL DICTIONARY ADD
  1231.               (setq reg-end (ispell-add-per-file-word-list
  1232.                      (car poss) reg-end)))
  1233.               ;; This prevents us from pointing out the word that was
  1234.               ;; just accepted (via 'i' or 'a') if it follows on the
  1235.               ;; same line. (The drawback of processing entire lines.)
  1236.               ;; Redo check following the accepted word.
  1237.               (if (and ispell-pdict-modified-p
  1238.                    (listp ispell-pdict-modified-p))
  1239.               ;; We have accepted or inserted a word. Re-check line
  1240.               (setq ispell-pdict-modified-p ; fix update flag
  1241.                 (car ispell-pdict-modified-p)
  1242.                 ispell-filter nil ; don't continue check.
  1243.                 end word-end))) ; reposition continue location
  1244.              (replace        ; STRING REPLACEMENT for this word.
  1245.               (delete-region word-start word-end)
  1246.               (insert replace)
  1247.               (let ((change (- (length replace) (length (car poss)))))
  1248.             (setq reg-end (+ reg-end change)
  1249.                   offset-change (+ offset-change change)
  1250.                   end (+ end change)))))
  1251.             (message "continuing spelling check...")
  1252.             (sit-for 0)))
  1253.           (setq ispell-filter (cdr ispell-filter))))) ; finished with line
  1254.       (goto-char end))))
  1255.   (not ispell-quit))
  1256.   ;; protected
  1257.   (if (get-buffer "*Choices*")
  1258.       (kill-buffer "*Choices*"))
  1259.   (ispell-pdict-save)
  1260.   (if ispell-quit
  1261.       (progn
  1262.     (if (numberp ispell-quit) (goto-char ispell-quit))
  1263.     (set-mark reg-end)        ; preserve the region, so we can
  1264.     (setq ispell-quit nil)))    ; execute 'ispell-region' as next cmd.
  1265.   (message "Spell done.")))
  1266.  
  1267.  
  1268.  
  1269. (defun ispell-buffer () 
  1270.   "Check the current buffer for spelling errors interactively."
  1271.   (interactive)
  1272.   (ispell-region (point-min) (point-max)))
  1273.  
  1274.  
  1275. ;;; Interactive word completion.
  1276. ;;; Forces "previous-word" processing.  Do we want to make this selectable?
  1277.  
  1278. (defun ispell-complete-word ()
  1279.   "Look up word before or under point in dictionary (see lookup-words command)
  1280. and try to complete it.  Standard ispell choices are then available."
  1281.   (interactive)
  1282.   (let ((cursor-location (point))
  1283.     ispell-keep-choices-win
  1284.     (word (ispell-get-word nil "\\*")) ; force "previous-word" processing.
  1285.     start end possibilities replacement)
  1286.     (setq start (car (cdr word))
  1287.       end (car (cdr (cdr word)))
  1288.       word (car word)
  1289.       possibilities
  1290.       (or (string= word "")        ; Will give you every word
  1291.           (lookup-words (concat word "*") ispell-complete-word-dict)))
  1292.     (cond ((eq possibilities t)
  1293.        (message "No word to complete"))
  1294.       ((null possibilities)
  1295.        (message "No match"))
  1296.       (t                ; There is a modification...
  1297.        (unwind-protect
  1298.        (progn
  1299.          (if ispell-highlight-p
  1300.          (highlight-spelling-error start end t)) ; highlight word
  1301.          (setq replacement (ispell-choose possibilities nil word)))
  1302.        ;; protected
  1303.        (if ispell-highlight-p
  1304.            (highlight-spelling-error start end))) ; un-highlight
  1305.        (cond
  1306.         ((equal 0 replacement)    ; BUFFER-LOCAL ADDITION
  1307.          (ispell-add-per-file-word-list word))
  1308.         (replacement        ; REPLACEMENT WORD
  1309.          (delete-region start end)
  1310.          (setq word (if (atom replacement) replacement (car replacement))
  1311.            cursor-location (+ (- (length word) (- end start))
  1312.                       cursor-location))
  1313.          (insert-string word)
  1314.          (if (not (atom replacement)) ; recheck spelling of replacement.
  1315.          (progn
  1316.            (goto-char cursor-location)
  1317.            (ispell-word nil t)))))
  1318.        (if (get-buffer "*Choices*")
  1319.            (kill-buffer "*Choices*"))))
  1320.     (ispell-pdict-save)
  1321.     (goto-char cursor-location)))
  1322.  
  1323.  
  1324. ;;; **********************************************************************
  1325. ;;;             Buffer Local Functions
  1326. ;;; **********************************************************************
  1327.  
  1328.  
  1329. (defun ispell-accept-buffer-local-defs ()
  1330.   "Loads all buffer-local information, restarting ispell when necessary."
  1331.   (ispell-buffer-local-dict)        ; May kill ispell-process.
  1332.   (ispell-buffer-local-words)        ; Will initialize ispell-process.
  1333.   (ispell-buffer-local-parsing))
  1334.  
  1335.  
  1336. ;;; Currently ispell version 3.0.09 (beta) doesn't fully support the "~"
  1337. ;;; pipe mode command.  Should be fixed in the next release.
  1338.  
  1339. (defun ispell-buffer-local-parsing ()
  1340.   "Places ispell into parsing mode for this buffer.
  1341. This overrides the default parsing mode.
  1342. This includes latex/nroff modes and extended character mode."
  1343.   ;; (ispell-init-process) must already be called.
  1344.   (process-send-string ispell-process "!\n") ; Put process in terse mode.
  1345.   (if (memq major-mode ispell-tex-major-modes)
  1346.       (process-send-string ispell-process "+\n") ; set ispell mode to tex
  1347.     (process-send-string ispell-process "-\n"))    ; set mode to normal (nroff)
  1348.   (let ((extended-char-mode (ispell-get-extended-character-mode)))
  1349.     (if extended-char-mode
  1350.     (process-send-string (concat extended-char-mode "\n"))))
  1351.   (save-excursion
  1352.     (goto-char (point-min))
  1353.     ;; Uses last valid definition
  1354.     (while (search-forward ispell-parsing-keyword nil t)
  1355.       (let ((end (save-excursion (end-of-line) (point)))
  1356.         (case-fold-search t)
  1357.         string)
  1358.     (while (re-search-forward " *\\([^ \"]+\\)" end t)
  1359.       ;; space separated definitions.
  1360.       (setq string (buffer-substring (match-beginning 1) (match-end 1)))
  1361.       (cond ((string-match "latex-mode" string)
  1362.          (process-send-string ispell-process "+\n"))
  1363.         ((string-match "nroff-mode" string)
  1364.          (process-send-string ispell-process "-\n"))
  1365.         ((string-match "~" string) ; Set extended character mode.
  1366.          (process-send-string ispell-process (concat string "\n")))
  1367.         (t (message "Illegal Ispell Parsing argument!")
  1368.            (sit-for 2))))))))
  1369.  
  1370.  
  1371. ;;; Can kill the current ispell process
  1372.  
  1373. (defun ispell-buffer-local-dict ()
  1374.   "Does necessary local dictionary initialization.
  1375. This overrides a Local Variable definition.
  1376. Both should not be used to define a buffer-local dictionary."
  1377.   (save-excursion
  1378.     (goto-char (point-min))
  1379.     (let (end)
  1380.       ;; Override the local variable definition.
  1381.       ;; Uses last valid definition.
  1382.       (while (search-forward ispell-dictionary-keyword nil t)
  1383.     (setq end (save-excursion (end-of-line) (point)))
  1384.     (if (re-search-forward " *\\([^ \"]+\\)" end t)
  1385.         (setq ispell-local-dictionary
  1386.           (buffer-substring (match-beginning 1) (match-end 1)))))))
  1387.   (if (and ispell-local-dictionary
  1388.        (not (equal ispell-local-dictionary ispell-dictionary)))
  1389.       (ispell-change-dictionary ispell-local-dictionary)))
  1390.  
  1391.  
  1392. (defun ispell-buffer-local-words ()
  1393.   "Loads the buffer-local \"dictionary\" in the current buffer."
  1394.   (if (and ispell-buffer-local-name
  1395.        (not (equal ispell-buffer-local-name (buffer-name))))
  1396.       (progn
  1397.     (setq ispell-buffer-local-name nil)
  1398.     (ispell-kill-ispell t)))
  1399.   (ispell-init-process)
  1400.   (save-excursion
  1401.     (goto-char (point-min))
  1402.     (while (search-forward ispell-words-keyword nil t)
  1403.       (or ispell-buffer-local-name
  1404.       (setq ispell-buffer-local-name (buffer-name)))
  1405.       (let ((end (save-excursion (end-of-line) (point)))
  1406.         string)
  1407.     (while (re-search-forward " *\\([^ \"]+\\)" end t)
  1408.       (setq string (buffer-substring (match-beginning 1) (match-end 1)))
  1409.       (process-send-string
  1410.        ispell-process (concat "@" (buffer-substring (match-beginning 1)
  1411.                             (match-end 1))
  1412.                   "\n")))))))
  1413.  
  1414.  
  1415. ;;; returns optionally adjusted region-end-point.
  1416.  
  1417. (defun ispell-add-per-file-word-list (word &optional reg-end)
  1418.   "Adds new word to the per-file word list."
  1419.   (or ispell-buffer-local-name
  1420.       (setq ispell-buffer-local-name (buffer-name)))
  1421.   (if (null reg-end)
  1422.       (setq reg-end 0))
  1423.   (save-excursion
  1424.     (goto-char (point-min))
  1425.     (let (line-okay search done string)
  1426.       (while (not done)
  1427.     (setq search (search-forward ispell-words-keyword nil 'move)
  1428.           line-okay (< (+ (length word) 1 ; 1 for space after word..
  1429.                   (progn (end-of-line) (current-column)))
  1430.                80))
  1431.     (if (or (and search line-okay)
  1432.         (null search))
  1433.         (progn
  1434.           (setq done t)
  1435.           (if (null search)
  1436.           (progn
  1437.             (open-line 1)
  1438.             (setq string (concat comment-start " "
  1439.                      ispell-words-keyword))
  1440.             ;; in case the keyword is in the middle of the file....
  1441.             (if (> reg-end (point))
  1442.             (setq reg-end (+ reg-end (length string))))
  1443.             (insert string)
  1444.             (if (and comment-end (not (equal "" comment-end)))
  1445.             (save-excursion
  1446.               (open-line 1)
  1447.               (forward-line 1)
  1448.               (insert comment-end)))))
  1449.           (if (> reg-end (point))
  1450.           (setq reg-end (+ 1 reg-end (length word))))
  1451.           (insert (concat " " word)))))))
  1452.   reg-end)
  1453.  
  1454.  
  1455.  
  1456. ;;; LOCAL VARIABLES AND BUFFER-LOCAL VALUE EXAMPLES.
  1457.  
  1458. ;;; Local Variable options:
  1459. ;;; mode: name(-mode)
  1460. ;;; eval: expression
  1461. ;;; local-variable: value
  1462.  
  1463. ;;; Local Variables:
  1464. ;;; mode: emacs-lisp
  1465. ;;; comment-column: 40
  1466. ;;; ispell-local-dictionary: "english"
  1467. ;;; End:
  1468.  
  1469.  
  1470. ;;; THE ISPELL BUFFER-LOCAL VALUES
  1471.  
  1472. ;;; The following places this file in nroff parsing and extended char modes.
  1473. ;;; Local IspellParsing: nroff-mode ~nroff
  1474. ;;; Change IspellDict to IspellDict: to enable the following line.
  1475. ;;; Local IspellDict german
  1476. ;;; The following were automatically generated by ispell using the 'A' command:
  1477. ; Local IspellWords:  ispell defvar ispell-highlight-p ispell-check-comments
  1478. ; Local IspellWords:  ispell-query-replace-choices query-replace non-nil tib
  1479. ; Local IspellWords:  ispell-check-tib regexps ispell-tib-ref-beginning Regexp
  1480. ; Local IspellWords:  ispell-tib-ref-end ispell-keep-choices-win ispell-word
  1481. ; Local IspellWords:  ispell-choices-win-default-height ispell-program-name
  1482. ; Local IspellWords:  ispell-region ispell-alternate-dictionary
  1483.