[comp.emacs] Enhanced spell front end for GNU Emacs + detex + delatex

bengt@watale.waterloo.EDU (Bengt Martensson) (08/21/87)

This is a greatly enhanced GNU Emacs interface to spell(1), or a
similar non-interactive spell-checking program.  It applies a
mode-specific clean-up function to the buffer, and sends it to a
mode-specific spell-program, with mode-specific flags.  Optionally, it
can run in the background.  A personal dictionary is supported.  If
spelling-mistakes were caught, it enters a dialogue with the user,
allowing him to accept the word, edit it, calling ispell for near
misses, insert it in the personal dictionary, etc.  It can also issue
an operating system command for searching in an on-line dictionary.
Help is available.  Recursive edit is always available.  It also
allows interface to the program ispell, which recently has been posted
to comp.sources.unix (or comp.sources.misc?).  See the file
spell.README for more details.

detex and delatex functions are included.

Since I will leave what Pnews calls "the civilized world" within a few
days, this is not as well tested as I would like.  However, I consider
the alternative of keeping it for myself as worse.  Most likely, there
are some bugs left.

Below is a shar consisting of the files spell.README, spell.el, and
detex.el.  If you can't figure out how to unpack it, ask your nearest
unix-guru.

Enjoy!

Bengt Martensson

--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|--+--+-
Bengt Martensson                        bengt@watale.waterloo.edu
Dept. of Electrical Engineering         ...seismo!watmath!watale!bengt
University of Waterloo           CSNET: bengt%watale@waterloo.csnet
Waterloo, Ontario               BITNET: bengt@watlager.BITNET
CANADA N2L 3G1               ARPA: bengt%watale%waterloo.csnet@csnet-relay.ARPA
                             VOICE: + 1 519-885-1211 x3968, 519-746-5276 (home)
--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--|--+--+-


#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  spell.README spell.el detex.el
# Wrapped by bengt@kingcong on Thu Aug 20 19:55:47 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f spell.README -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"spell.README\"
else
echo shar: Extracting \"spell.README\" \(15124 characters\)
sed "s/^X//" >spell.README <<'END_OF_spell.README'
X-*-outline-*-
X
X* Overview
X
XThis is a greatly enhanced GNU Emacs interface to spell(1), or a
Xsimilar non-interactive spell-checking program.  It applies a
Xmode-specific clean-up function to the buffer, and sends it to a
Xmode-specific spell-program, with mode-specific flags.  Optionally, it
Xcan run in the background.  A personal dictionary is supported.  If
Xspelling-mistakes were caught, it enters a dialogue with the user,
Xallowing him to accept the word, edit it, calling ispell for near
Xmisses, insert it in the personal dictionary, etc.  It can also issue
Xan operating system command for searching in an on-line dictionary.
XHelp is available.  Recursive edit is always available.
X
XIt allows use of the program ispell, which recently has been posted to
Xcomp.sources.unix (or comp.sources.misc?).  If this program is not
Xavailable the only thing that will not work is the i-command in the
Xdialogue loop.
X
X** Installation
X
XProbably you should compile the lisp files and bind at least some of
Xthe user-level functions to keys.  Do ^Hf byte-compile-file, and ^Hf
Xdefine-key for information on how to do this.
X
XThis has been developed under GNU Emacs v 18.47.2.  A quick check
Xindicated that everything should work under version 17 also, provided
Xthat the lines containing mode-line-buffer-identification is removed
X(three occurrences).
X
X** Acknowledgements
X
XThis code is derived from the original GNU-Emacs source in spell.el
X(although not very much remains...).  The function spell-ispell-word
Xis a very light modification of the function ispell-word, stolen from
Xispell.el in the ispell-distribution.  It is written by some convex
Xcombination of Walt Buehring, Perry Smith, Mark Davies and Andrew
XVignaux.
X
XThe functions in the file detex.el are not derived.  It, and this
Xdocument, can be distributed on the same terms as GNU Emacs.
X
X* User functions
X
X** spell-buffer
X
X*** mode-specific cleaning-up function 
X
XThe function spell-buffer first applies a user selectable,
Xmode-specific clean-up function to the buffer.  For example, if the
Xmajor mode of the current buffer is latex-mode, the lisp function
Xdelatex is applied to the buffer, removing all latex-specific
Xconstruction that probably should not be checked against spelling
Xmistakes.  (See more on this is the section on mode-specific clean-up
Xfunctions.)  After that, the buffer is sent to a spell-process.  This
Xagain is user-selectable and (possibly) mode-specific.  Then, if any
Xspelling mistake was caught, it enters a dialogue loop with the user.
X
X*** correcting dialogue
X
XFor each word the spelling program did not find, the user is asked to
Xenter one of the alternatives:
X
X(a)ccept, (e)dit, (h)elp, (i)spell, (I)nsert, (r)ec-edit, (s)earch.  
X
X(a)ccept (or SPACE) will accept the word for this session.  
X(e)dit will let you edit the word in the minibuffer, and then (provided that
X   the word is changed), enters a query-replace.
X(h)elp (or "?") displays a help message.
X(i)spell executes the function ispell-word, and if ispell finds close correct
X   words, you can enter the character associated with the alternative to
X   insert that word instead of the misspelled one.  Enter SPACE to leave
X   unchanged.  If ispell does not find any close matches, this is
X   equivalent to e.
X(I)nsert will insert the word in your personal dictionary, 
X   by default ~/.mywords 
X(r)ec-edit will embark on a recursive edit (C-M-c to exit).  This
X   allow you to temporarily interrupt the correcting session.
X(s)earch will prompt for a regular expression and search for it in an
X   asynchronous process.  By default, this uses grep(1) to search in
X   /usr/dict/words. 
X
XThere is no explicit abort-command.  Use ^G instead.
X
X*** Running in the background
X
XIf the global variable spell-wait is nil, spell-buffer will start the
Xspell-program running in an asynchronous shell, thus letting the user
Xdoing something else while the process continues.  Giving spell-buffer
Xa negative argument forces spell-wait nil, thus making it run in the
Xbackground.  Giving it an argument larger than 1 forces spell-wait t,
Xwhile the argument 0 toggles its value.
X
XWhen the asynchronous process has finished, the user has to issue the
Xcommand correct-spelling explicitly.  This enters the same correcting
Xdialogue as described above.
X
X** spell-region
X
XThe command spell-region is analogous to spell-buffer.  It applies to
Xthe region instead of the buffer.
X
X** spell-word
X
Xspell-word checks the spelling of the word at or before point.  The
Xmode-specific clean-up function and spell-command corresponding to
Xfundamental-mode will be used.  If not found by the spelling program,
Xit enters the same correction dialogue as described in spell-buffer.
XIgnores the value of spell-wait, so that it never runs asynchronously.
X
X** spell-string
X
Xspell-string is essentially the same function as in the standard spell
Xin GNU Emacs.  It prompts the user for a string, typically consisting
Xof several words, and checks the spelling of the string.  The words
Xnot found are reported.  No correcting dialogue is entered.
X
XThe spell-command corresponding to fundamental-mode will be used.  No
Xclean-up function will be applied.
X
X** correct-spelling
X
XIf spell-region or spell-buffer runs asynchronously, Emacs is not told
Xwhen the process finishes.  After the process has finished, the user
Xissues the command correct-spelling, which will enter the same
Xcorrecting dialogue as describe under spell-buffer.  There is no need
Xto select the correct buffer first.
X
X* The personal dictionary
X
XDuring the correcting dialogue, each word the spell program did not
Xfind is first checked agains the private dictionary.  This is by
Xdefault a file named ~/.mywords.  It is an unsorted file with one word
Xper line.  The words in the personal dictionary are considered case
Xinsensitive; I don't know whether this is a good idea or not.  If new
Xwords are inserted during the correcting dialogue, the buffer is of
Xcourse marked modified, and should probably be saved.  The functions
Xin the posting do not save it automatically.
X
XIt is advisable to keep this file fairly short, since otherwise it
Xwill slow down the correcting dialogue considerably.  This is fairly
Xunpleasant, since it is in the middle of a dialogue where the user is
Xsupposed to think and make decisions, and therefore tends to be fairly
Xsensitive for delays.  A larger collection of personal words can most
Xoften be added to the spelling program (in the case of spell(1), see
Xspellin(1) and spellout(1)).
X
X* Dependence of operating system features
X
XWe next describe the dependence of the program the spell-interface
Xuses.  For a fuller description, the reader is referred to the source
Xcode. 
X
X** spell
X
XAny reasonable spell-checking program can be used with this spell
Xinterface.  The only requirement is that it takes a file of words as
Xinput and produces a file of "misspelled" word as output, with one
Xword per line.  If the program is good at filtering out mode-specific
Xconstructions, the lisp-function can be reduced, thereby speeding up
Xthe execution.
X
X*** other operating systems (read: VMS)
X
XAs far as I am aware of, call-process-region does not work under
XVMS--at least it did not under version 17.  spell-string depends on
Xthis, and also the other spell-xxx functions WHEN running in
Xwait-mode.  If spell-wait is nil and an appropriate spelling checker
Xis present, I am almost sure of that spell-region, spell-buffer, and
Xcorrect-spelling will work with no or trivial modification.  Probably
Xit would be a good idea to make all asynchronous processes to use the
Xsame shell window, VMS is not exactly known for its ability to handle
Xmultiple processes.  (My VMS account, on a VAX 11/785, has a
Xsubprocess quota of 2 (is it :-( or :-)?).)
X
XProbably you would also like to change spell-directory, cd-string,
Xsemi-colon, redirection-string, etc also.
X
X** ispell
X
XIf ispell is not installed the i-command in the correcting dialogue
Xwill not work.  Period.  Nothing else depends on it.  For replacing it
Xwith another close-match generator, see ispell.el or the documentation
Xof ispell.  (Sorry that these pointers are somewhat circular.)
X
X*** other operating systems (read: VMS)
X
XHighly non-trivial.
X
X** searches, grep etc
X
XWhen the s-command is given in the correcting dialogue an operating
Xsystem command is constructed by concatenating
Xspell-search-first-string (by default "grep"), the user entered
Xstring, and spell-search-last-string (by default "/usr/dict/words").
XFrom this description, it should be obvious how to use other programs
Xand dictionaries.  The process runs in a separate buffer.
X
XOr you can have this command to do something entirely different...
X
X*** other operating systems (read: VMS)
X
XShould work with "search", possibly with some qualifiers.  Also, see
Xthe remark on several processes above.
X
X* Mode-specific clean-up functions
X
XThis posting comes with several lisp functions, intended to do a
Xmode-dependent clean-up.  In general, there are two possibilities for
Xdoing this: Using a lisp-function in emacs, or letting the external
Xspelling program do it.  For example, at Waterloo there are -l and -t
Xoptions for spell(1), which uses delatex and detex filters instead of
Xthe default deroff.  These are far from perfect (the lex-programs on
Xthe Unix TeX distribution tape, right?)  The detex and delatex
Xfunctions provided here are very complete, easily user customizable
X(see below) but slow (possibly even SLOW...).  For a more efficient
Xapproach, the lisp function should be trimmed down to only what the
Xspell program doesn't.  May I ask that if some person checks my detex
Xand delatex against the lex-code, he/she would posts the minimal lisp
Xfunctions that do only what the lex program is not handling well.
X
XFeel free to use or not to use the functions presented here.  The list
Xspell-options contains the functions and spell commands for different
Xmodes.  See doc-string.
X
X** detex
X
Xdetex removes all math constructions including displays.  Then it
Xremoves comments, font declarations, some comments together with their
Xargument (given in the list detex-tex-commands-with-garbage-argument),
Xall discretionary hyphens, and finally all remaining TeX commands.
X
X*** customization
X
XThe list detex-tex-commands-with-garbage-argument can be changed.  If
Xyou are using a TeX-dialect with another escape character that "\" and
Xother grouping characters than the curly brackets, re-define the
Xfunction define-escape-and-group.  See the doc-strings.
X
X*** limitation
X
XProbably also words with accented characters (of the type \"o) and
Xcontaining macros such as \aa should be junked too.
X
X** delatex
X
Xdelatex is essentially a superset of detex.  It understands latex
Xmath-construction, and also the "$$" displays (which are not part of
Xofficial latex).  It also removes all environments found in the list
Xdetex-latex-garbage-environments.  It uses the list
Xdetex-latex-commands-with-garbage-argument for determining which
XTeX-commands to junk together with its arguments.
X
X*** customization
X
XShould be obvious.  For example, if you define a macro in LaTeX, say 
X
X\newcommand{\eqnref}[1]{(\ref #1)}
X
Xyou should probably add "eqnref" to the list
Xdetex-latex-commands-with-garbage-argument.
X
X** programs
X
XJust to demonstrate the power of mode-specific lisp-functions I hacked
Xup the functions only-double-quote-strings and
Xonly-single-quote-strings, which will junk everything except for
Xstrings enclosed in double or single quotes respectively.  The
Xdefault list (cooked up fairly randomly) calls
Xonly-double-quote-strings in the modes emacs-lisp-mode, lisp-mode,
Xc-mode; while only-single-quote-strings are called in pascal-mode.
X
X** mail
X
XThe list in this posting calls the function kill-mail-header from
Xmail-mode.  This kills the header of the mail-buffer up to and
Xincluding the current value of mail-header-separator.
X
X* Customization
X
XHow to use other spell-checkers, near-match generators, or search
Xprograms has already been described.  We only describe some of the
Xother user-set-able options.  See the doc-strings!
X
X** spell-options
X
XThe list spell-options contains the mode-specific functions and
Xfilters.  See the doc-string.  Note that it must contain
Xfundamental-mode, since this acts as default.
X
X** spell-cautious
X
XAfter a query-replace, the user is asked if he wants to continue
X(SPACE), redo (-) or enter recursive edit (r).  Putting spell-cautious
Xnil will disable this.
X
X** more options
X
XSee the source.
X
X* Tips
X
XIt is a good idea to run the correction dialogue in a smaller window,
Xsince Emacs displays the misspelled word in its proper place, thereby
Xprobably rewriting the current window.  (I did not want to make this
Xautomatic since there are almost too many bells and whistles already.)
X
XThe recursive edit is a very nice feature.  Say for example that you
Xhave started the correction dialogue in a full screen window, and
Xthen--in accordance with the previous paragraph--want a smaller.  Do
Xr, ^X-2, ^X-2, ESC-^C, will do it for you.
X
XNote that the personal dictionary is a standard editable text-file.
XNotice the warning on large personal dictionaries.  This is not the
Xplace to keep a large personal dictionary.
X
XThe i-option in the dialogue is slow the first time, but subsequent
Xcalls are blindingly fast.  In general, this is much faster than you
Xcan edit the word with the e-option.
X
X* Bugs/restrictions/todo/problems/discussion
X
XThe limitation in detex should be fixed.  
X
XShould possibly allow a file-specific dictionary, together with the
Xone on your main directory.
X
XSlow with large personal dictionary; can it be searched more efficiently?
X
XSearch in personal dictionary case insensitive, is this good or bad?
X
XDoesn't always find location when redo.
X
XSince it checks in the private dictionary in the correction phase, the
Xmessage to the user is a little funny in the case where all words
Xspell does not like are in the personal word list.
X
XEven if only a word or the region is checked, the query replace takes
Xplace in the entire buffer.  I think this is good.
X
Xspell(1) considers for example "comp.sources.unix" as one (misspelled)
Xword.  I consider this as correct behavior.
X
XThere should be an "-r" (raw) option in spell(1), which surpassed
Xderoff.  When passed this document, deroff turns "\newcommand" into
X"wcommand"... Arrrgh!!
X
XPossibly an explicit exit alternative should be presented in the
Xcorrecting dialogue.
X
XI am not sure whether I like ispell-word's habit to echo words in
Xupper-case.
X
XPossibly there should be the alternative scroll-other-window in the
Xcorrecting dialogue.  (However, this can be done by first entering
Xrecursive search.)  There is a problem with this, namely that the
Xfunction scroll-other-window might signal an error if close to end of
Xthe buffer, in which case the correcting dialogue is aborted.  This
Xmust be prevented.
X
XIn a previous version, the spell-shell window was made small.  I took
Xthis feature out because, for example help would later use the same
Xwindow... I have left it out-commented if anyone wants it.
X
XThis document should be more carefully written, with a simple
Xhow-to-use-it guide separated from the more technical information.
END_OF_spell.README
if test 15124 -ne `wc -c <spell.README`; then
    echo shar: \"spell.README\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f spell.el -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"spell.el\"
else
echo shar: Extracting \"spell.el\" \(18649 characters\)
sed "s/^X//" >spell.el <<'END_OF_spell.el'
X;; Spelling correction interface for Emacs.
X;; Copyright (C) 1985 Richard M. Stallman.
X;; This file is part of GNU Emacs.
X;;
X;; Almost completely rewritten by Bengt Martensson
X;; LastEditDate "Thu Aug 20 19:50:16 1987"
X     
X;; GNU Emacs is distributed in the hope that it will be useful,
X;; but WITHOUT ANY WARRANTY.  No author or distributor
X;; accepts responsibility to anyone for the consequences of using it
X;; or for whether it serves any particular purpose or works at all,
X;; unless he says so in writing.  Refer to the GNU Emacs General Public
X;; License for full details.
X     
X;; Everyone is granted permission to copy, modify and redistribute
X;; GNU Emacs, but only under the conditions described in the
X;; GNU Emacs General Public License.   A copy of this license is
X;; supposed to have been given to you along with GNU Emacs so you
X;; can know your rights and responsibilities.  It should be in a
X;; file named COPYING.  Among other things, the copyright notice
X;; and this notice must be preserved on all copies.
X     
X(defvar spell-options '((text-mode nil "spell")
X			(mail-mode kill-mail-header "spell")
X			(tex-mode detex "spell -t")
X			(latex-mode delatex "spell -l")
X			(emacs-lisp-mode only-double-quote-strings "spell")
X			(lisp-mode only-double-quote-strings "spell")
X			(c-mode only-double-quote-strings "spell")
X			(pascal-mode only-single-quote-strings "spell")
X			(fundamental-mode nil "spell")) 
X  "*List of options for spell.  Each element is a list whose first element
Xis the name of a major mode, the second element is nil or the name of
Xa lisp-function to apply before the spell command is given, and the
Xthird is the actual spell-command given to the shell.  MUST contain
Xfundamental-mode, which plays the role of default.")
X(defvar spell-wait t
X  "*If nil spell-region and spell-buffer will run in a separate process.")
X(defvar personal-word-list "~/.mywords"
X  "*Name of the personal, unsorted word list.")
X(defvar spell-cautious t
X  "*Setting this non-nil will give inquire the user for continuation in some 
Xinstances when running correct-spelling.")
X(defvar spell-search-first-string "egrep"
X  "First part of command to search for word.  The regular expression to search
Xfor goes after this.")
X(defvar spell-search-last-string "/usr/dict/words"
X  "Last part of command to search for word.  The regular expression to search
Xfor goes before this.")
X;;(defconst spell-window-height 4 "Height of the window for running spell.")
X(defconst spell-directory "/tmp/" "Directory to place the temporary files in.")
X(defconst cd-string "cd" "String to change the name of default directory.")
X(defconst semi-colon ";" "Make this "";"" in unix, ""\\n"" otherwise.")
X(defconst redirection-string ">" "To redirect output.")
X(defvar spell-in-file "" "Temporary file for running spell from EMACS")
X(setq spell-in-file (make-temp-name (concat spell-directory "spelli")))
X(defvar spell-out-file "" "Temporary file for running spell from EMACS")
X(setq spell-out-file (make-temp-name (concat spell-directory "spello")))
X(defvar personal-buffer nil "Buffer to hold the personal dictionary.")
X(defvar spell-last-buffer nil "Last buffer in which spell was invoked.")
X
X(autoload 'detex "detex")
X(autoload 'delatex "detex")
X
X(defun only-double-quote-strings ()
X  "Deletes everything except for strings enclosed in double quotes ("")"
X  (only-strings """"))
X
X(defun only-single-quote-strings ()
X  "Deletes everything except for strings enclosed in single quotes (')"
X  (only-strings "'"))
X
X(defun only-strings (ch)
X  "Deletes everything except for string delimited by CH."
X  (let ((beg (point-min)))
X    (goto-char (point-min))
X    (while (search-forward ch (point-max) t)
X      (backward-char 1)
X      (delete-region beg (point))
X      (forward-sexp 1)
X      (insert-string "\n")
X      (setq beg (point)))
X    (delete-region beg (point-max))))
X
X(defun kill-mail-header ()
X  "Kills the mail header up to and including ""--text follows this line--""."
X  (goto-char (point-min))
X  (if (search-forward mail-header-separator (point-max)  t)
X      (delete-region (point-min) (point))))
X
X(defun cadr (l)
X  (car (cdr l)))
X(defun caddr (l)
X  (car (cdr (cdr l))))
X
X(defun size-of (&optional buf)
X  "Returns the size of the current buffer, or BUFFER if on optional argument 
Xis given"
X  (save-excursion
X    (if buf (set-buffer buf))
X    (buffer-size)))
X
X(defun non-empty-buffer (&optional buf)
X  "Returns nil iff the current buffer, or BUFFER if an argument is given,
Xhas a size of 0."
X  (> (size-of buf) 0))
X
X(defun spell-string (string)		; (almost) the original one
X  "Check spelling of string supplied as argument."
X  (interactive "sSpell string: ")
X  (let* ((buf (get-buffer-create " *temp*"))
X	 (spell-command
X	  (caddr (assoc 'fundamental-mode spell-options)))
X	 (spell-program
X	  (substring spell-command 0 (string-match " " spell-command)))
X	 (spell-flags (if (string-match " " spell-command)
X			  (substring spell-command
X				     (string-match " " spell-command))
X			"")))
X    (save-excursion
X      (set-buffer buf)
X      (widen)
X      (erase-buffer)
X      (insert string "\n")
X      (call-process-region (point-min) (point-max)
X			   spell-program t t nil spell-flags)
X      (if (= 0 (buffer-size))
X	  (message "%s is correct" string)
X	(goto-char (point-min))
X	(while (search-forward "\n" nil t)
X	  (replace-match " "))
X	(message "%sincorrect" (buffer-substring 1 (point-max)))))))
X
X(defun spell-word ()
X  "Check spelling of word at or before point.
XIf it is not correct, ask user for the correct spelling in the same way as
Xcorrect-spelling."
X  (interactive)
X  (let (beg end)
X    (save-excursion
X      (if (not (looking-at "\\<"))
X	  (forward-word -1))
X      (setq beg (point))
X      (forward-word 1)
X      (setq end (point)))
X    (spell-region beg end nil (buffer-substring beg end) t)))
X
X(defun spell-buffer (wait-code)
X  "Check spelling of every word in the buffer.
XFor each incorrect word, you are asked for the correct spelling,
Xas in correct-spelling.
X
XIf spell-wait is nil, the spell program will run in the background in
Xa separate window, and you will have to issue the command correct-spelling
Xto correct.  Calling spell-buffer with an argument larger that 1 will 
Xforce spell-wait t, while a negative argument will make spell-wait nil.  
XAn argument of 0 toggles its value."
X  (interactive "p")
X  (spell-region (point-min) (point-max) wait-code "buffer"))
X
X(defun spell-region (start end &optional wait-code description wait)
X  "Like spell-buffer but applies only to region.
XFrom program, applies from START to END."
X  (interactive "r\np")
X  (if wait-code (cond ((> wait-code 1) (setq spell-wait t))
X		      ((= wait-code 0) (setq spell-wait (not spell-wait)))
X		      ((< wait-code 0) (setq spell-wait nil))))
X  (let ((mode (if wait			; wait is t <=> call from spell-word
X		  'fundamental-mode	; use fundamental mode in that case
X		major-mode))
X	(oldbuffer (current-buffer))
X	(buf (get-buffer-create "*spell-temp*")))
X    (message "Checking spelling of %s..." (or description "region"))
X    (save-excursion
X      (set-buffer buf)
X      (widen)
X      (erase-buffer)
X      (insert-buffer-substring oldbuffer start end)
X      (run-spell-on-buffer buf mode description wait))
X    (if (and (or wait spell-wait)
X	     (non-empty-buffer buf))
X	(correct-spelling buf)
X      (setq spell-last-buffer (current-buffer)))
X    (kill-buffer buf)))
X
X(defun run-spell-on-buffer (buf mode description force-wait)
X  "First runs the mode-specific function given in spell-options.
XThen runs spell on the buffer by calling either wait-spell-buffer or
Xnowait-spell-buffer.  The buffer will be destroyed."
X  (let (command)
X    (set-buffer buf)
X    (if (and (assoc mode spell-options)	; Mode specific lisp function
X	     (cadr (assoc mode spell-options)))
X	(progn
X	  (message "Applying %s..." (cadr (assoc mode spell-options)))
X	  (funcall (cadr (assoc mode spell-options)))))
X    (if (or force-wait spell-wait)
X	(wait-spell-buffer buf mode description)
X      (nowait-spell-buffer buf mode))))
X
X(defun nowait-spell-buffer (buf mode)
X  "Starts an asynchronous spell-process in another window (provided it does
Xnot already exist)."
X  (write-region (point-min) (point-max) spell-in-file)
X  (or (get-buffer "*spell-shell*")
X      (progn
X	(shell)
X	(rename-buffer "*spell-shell*")
X	(message "%s" "Creating new shell...")
X	(bury-buffer)
X	(sit-for 5)			; Sorry ...
X	(message "%s" "")))
X  (send-string "*spell-shell*"
X	       (concat cd-string " " spell-directory semi-colon))
X  (setq command (if (assoc mode spell-options)
X		    (caddr (assoc mode spell-options))
X		  (caddr (assoc 'fundamental-mode spell-options))))	    
X  (send-string "*spell-shell*"
X	       (concat command " " spell-in-file
X		       redirection-string spell-out-file "\n"))
X  (pop-to-buffer "*spell-shell*")
X  ;;(enlarge-window (- spell-window-height (window-height)))
X  (goto-char (point-max))
X  (recenter 0)
X  (other-window -1))
X
X(defun wait-spell-buffer (buf mode &optional description)
X  "Applies the mode-specific spell command to the buffer."
X  (let* ((spell-command (or (caddr (assoc mode spell-options))
X			    (caddr (assoc 'fundamental-mode spell-options))))
X	 (spell-program
X	  (substring spell-command 0 (string-match " "spell-command)))
X	 (spell-flags (if (string-match " "spell-command)
X			  (substring spell-command
X				     (string-match " "spell-command))
X			"")))
X    (message "Checking spelling of %s..." (or description "region"))
X    (set-buffer buf)
X    (goto-char (point-max))
X    (insert "\n")
X    (call-process-region (point-min) (point-max)
X			 spell-program t t nil spell-flags)
X    (message "Checking spelling of %s...%s"
X	     (or description "region")
X	     (if (non-empty-buffer buf)
X		 ""
X	       "correct"))))
X
X(defun spell-help ()
X  "Generates a help text for correct-spelling."
X  (let ((this-window (selected-window)))
X    (switch-to-buffer-other-window (get-buffer-create "*Help*"))
X    (erase-buffer)
X    (insert
X     "For each incorrect word, you are asked for one of the alternatives
X(a)ccept, (e)dit, (h)elp, (i)spell, (I)nsert, (r)ec-edit, (s)earch.  
X(a)ccept (or SPACE) will accept the word for this session.
X(e)dit will let you edit the word in the minibuffer, and then (provided that
X   the word is changed), enters a query-replace.
X(h)elp (or ""?"") displays this help message.
X(i)spell executes the function ispell-word, and if ispell finds close correct
X   words, you can enter the character associated with the alternative to
X   insert that word instead of the misspelled one.  SPACE to leave unchanged.
X(I)nsert will insert the word in your personal dictionary.
X(r)ec-edit will embark on a recursive edit (C-M-c to exit).
X(s)earch will prompt for a regular expression and search for it in an
Xasynchronous process.")
X    (goto-char (point-min))
X    (select-window this-window)))
X
X(defun spell-recursive-edit ()
X  "Enters a recursive edit within a save excursion.  Also displays a 
Xhopefully helpful text."
X  (message "Entering recursive edit.  C-M-c to exit.")
X  (save-excursion (recursive-edit)))
X
X(defun spell-continue (&optional str)
X  "Prompts the user for options on how to continue.  Allows recursive
Xedits.  Returns nil if any other character than e or SPACE is entered.
XDoes nothing if spell-cautious is nil and no argument is supplied."
X  (if (or spell-cautious str)
X      (let (c)
X	(if str
X	    (message (concat str ".  Press SPACE to continue, - to redo, r to  recursive edit"))
X	  (message "Press SPACE to continue, - to redo, r to enter recursive edit"))
X	(setq c (read-char))
X	(cond ((equal c ?r) (spell-recursive-edit) t)
X	      ((equal c ? ))
X	      (t nil)))))
X
X(defun not-in-personal-list (word)
X  "Returns non-nil if WORD is not found in the personal dictionary."
X  (not (save-excursion
X	 (set-buffer personal-buffer)
X	 (goto-char (point-min))
X	 (word-search-forward word (point-max) t))))
X
X(defun edit-it (word &optional replacement)
X  "Edits WORD in the minibuffer and, if given a different spelling,
Xreplaces it by a query-replace.  If optional argument REPLACEMENT is given,
Xuses this instead of editing." 
X  (let (newword
X	(case-fold-search t)
X	(case-replace t))
X    (setq newword (or replacement
X		      (read-from-minibuffer
X		       (concat "Replacement for " word ": ") word)))
X    (if (equal word newword)
X	()
X      (goto-char (point-min))
X      (query-replace-regexp (concat "\\b" (regexp-quote word) "\\b") newword))
X    (spell-continue)))
X
X(defun spell-search (&optional word)
X  "Issues a command in an asynchronous process.  The command is composed
Xby, in order, spell-search-first-string, a string supplied by the user,
Xand spell-search-last-string."
X  (interactive)
X  (let ((this-window (selected-window))
X	(str (read-from-minibuffer "Enter string to search for: " word)))
X    (or (get-buffer "*spell-search*")
X	(progn
X	  (shell)
X	  (rename-buffer "*spell-search*")
X	  (message "%s" "Creating new shell...")
X	  (bury-buffer)
X	  (sit-for 5)			; Sorry ...
X	  (message "%s" "")))
X    (switch-to-buffer-other-window "*spell-search*")
X    (sit-for 0)
X    (erase-buffer)
X    (send-string "*spell-search*"
X		 (concat spell-search-first-string " '" str "' "
X			 spell-search-last-string "\n"))
X    (goto-char (point-max))
X    (recenter 0)
X    (select-window this-window)))
X
X(defun spell-insert (word)
X  "Inserts WORD in the personal dictionary."
X  (save-excursion
X    (set-buffer personal-buffer)
X    (goto-char (point-max))
X    (insert word "\n")
X    (message (concat "Inserted """ word """ in " personal-word-list))
X    (sit-for 1)))
X
X(defun spell-dialogue (word)
X  "Implements the dialogue with the user described in correct-spelling"
X  (let (command)
X    (goto-char (point-min))
X    (if (not (word-search-forward word (point-max) t))
X	(spell-continue (concat "Cannot find " word))
X      (message (concat word ": (a)ccept, (e)dit, (h)elp, (i)spell, (I)nsert, (r)ec-edit, (s)earch?"))
X      (setq command (read-char))
X      (if (not (cond ((equal command ?a)) ; Do nothing
X		     ((equal command ? ))
X		     ((equal command ?e) (edit-it word))
X		     ((equal command ?i)
X		      (edit-it word (spell-ispell-word word)))
X		     ((equal command ?I) (spell-insert word))
X		     ((equal command ?r) (spell-recursive-edit) nil)
X		     ;;((equal command ?S) (scroll-other-window nil) nil)
X		     ((equal command ?s) (spell-search) nil)
X		     ((equal command ?h) (spell-help) nil)
X		     ((equal command ??) (spell-help) nil)
X		     (t (ding) nil)))
X	  (spell-dialogue word)))))
X		     
X(defun correct-spelling (&optional buf)
X  "Corrects the spelling by the file generated by spell. This is done
Xin an interactive dialogue.
X     
XFor each incorrect word, you are asked for one of the alternatives
X(a)ccept, (e)dit, (h)elp, (i)spell, (I)nsert, (r)ec-edit, (s)earch.  
X(a)ccept (or SPACE) will accept the word for this session.
X(e)dit will let you edit the word in the minibuffer, and then (provided that
X   the word is changed), enters a query-replace.
X(h)elp (or ""?"") displays this help message.
X(i)spell executes the function ispell-word, and if ispell finds close correct
X   words, you can enter the character associated with the alternative to
X   insert that word instead of the misspelled one.  SPACE to leave unchanged.
X(I)nsert will insert the word in your personal dictionary.
X(r)ec-edit will embark on a recursive edit (C-M-c to exit).
X(s)earch will prompt for a regular expression and search for it in an
X   asynchronous process."
X  (interactive)
X  (let (word
X        (old-mode-line-buffer-identification mode-line-buffer-identification)
X        (errors-buffer (if buf
X        		   buf
X			   (if (file-readable-p spell-out-file)
X			       (find-file-noselect spell-out-file)
X			       (error "Can't read or find output file from spell (%s)" spell-out-file)))))
X    (save-excursion
X      (setq mode-line-buffer-identification (list "Spell: %17b"))
X      (setq personal-buffer (find-file-noselect personal-word-list))
X      (if (not buf) (switch-to-buffer spell-last-buffer))
X      (unwind-protect
X        (while (non-empty-buffer errors-buffer)
X          (save-excursion
X	    (set-buffer errors-buffer)
X	    (goto-char (point-min))
X	    (setq word
X		  (buffer-substring (point) (progn (end-of-line) (point))))
X	    (forward-char 1)
X	    (delete-region (point-min) (point)))
X	  (if (and (not (equal word "")) (not-in-personal-list word))
X            (spell-dialogue word)))
X        (setq mode-line-buffer-identification
X              old-mode-line-buffer-identification)
X        (set-buffer errors-buffer)
X        (not-modified)
X        (kill-buffer errors-buffer)
X        (message "Finished!!")
X        (if (not buf) (progn
X	  	        (delete-file spell-in-file)
X		        (delete-file spell-out-file)))))))
X
X(autoload 'ispell-init-process "ispell")
X
X;;; This function is modified in two respects:
X;;; ispell-init-process is moved in order to autoload correctly, and
X;;; the function returns nil or the selected alternative, as a string.
X
X(defun spell-ispell-word (&optional quietly)
X  "Check spelling of word at or before dot.
XIf word not found in dictionary, display possible corrections in a window 
Xand let user select."
X  (interactive)
X  (let* ((current-syntax (syntax-table))
X	 start end word poss replace)
X    (ispell-init-process)		; erases ispell output buffer
X    (unwind-protect
X	(save-excursion
X	  ;; Ensure syntax table is reasonable 
X	  (set-syntax-table ispell-syntax-table)
X	  ;; Move backward for word if not already on one.
X	  (if (not (looking-at "\\w"))
X	      (re-search-backward "\\w" (point-min) 'stay))
X	  ;; Move to start of word
X	  (re-search-backward "\\W" (point-min) 'stay)
X	  ;; Find start and end of word
X	  (or (re-search-forward "\\w+" nil t)
X	      (error "No word to check."))
X	  (setq start (match-beginning 0)
X		end (match-end 0)
X		word (buffer-substring start end)))
X      (set-syntax-table current-syntax))
X    ;;(ispell-init-process)		; erases ispell output buffer
X    (or quietly (message "Checking spelling of %s..." (upcase word)))
X    (save-excursion
X      (set-buffer ispell-out-name)
X      (send-string ispell-process (concat word "\n"))
X      ;; wait until we have a complete line
X      (while (progn
X	       (goto-char (point-max))
X	       (/= (preceding-char) ?\n))
X	(accept-process-output ispell-process))
X      (goto-char (point-min))
X      (setq poss (ispell-parse-output
X		  (buffer-substring (point) 
X				    (progn (end-of-line) (point))))))
X    (cond ((eq poss t)
X	   (or quietly (message "Found %s" (upcase word))))
X	  ((stringp poss)
X	   (or quietly (message "Found it because of %s" (upcase poss))))
X	  ((null poss)
X	   (or quietly (message "Could Not Find %s" (upcase word))))
X	  (t (setq replace (ispell-choose poss word))))
X    replace))
END_OF_spell.el
if test 18649 -ne `wc -c <spell.el`; then
    echo shar: \"spell.el\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f detex.el -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"detex.el\"
else
echo shar: Extracting \"detex.el\" \(7609 characters\)
sed "s/^X//" >detex.el <<'END_OF_detex.el'
X;;; Detex
X;;; Bengt Martensson
X;;; LastEditDate="Tue Aug 18 17:51:42 1987"
X     
X;;; For redistribution the same rules apply as for GNU Emacs.
X
X(defvar detex-tex-commands-with-garbage-argument
X  '("input" "special" "message")
X  "List of TeX-commands for which their argument should be filtered out
Xby detex.")
X
X(defvar detex-latex-commands-with-garbage-argument
X  (append detex-tex-commands-with-garbage-argument
X	  '("documentstyle" "pagestyle" "thispagestyle" 
X	    "pagenumbering" "typeout" "typein" "label" "ref" "pageref"
X	    "bibliography" "bibitem" "bibliographystyle"
X	    "cite" "include" "includeonly" "begin" "end"))
X    "List of LaTeX-commands for which their argument should be filtered out
Xby delatex.")
X
X(defvar detex-latex-garbage-environments
X  '("array" "equation" "eqnarray" "eqnarray*" "verbatim" "displaymath")
X  "List of LaTeX environments which will be deleted by delatex.")
X
X(defvar detex-tex-escape "" "TeX escape character used by detex.")
X(defvar detex-re-tex-escape ""
X  "Regular expression for TeX escape character.  Used by detex.")
X(defvar detex-not-tex-escape ""
X  "Regular expression that matches anything but TeX escape character.
XUsed by detex.")
X(defvar detex-latex-optional-left ""
X  "LaTeX character denoting left bracket for optional argument.
XUsed by detex.")
X(defvar detex-tex-left "" "TeX left group character used by detex.")
X(defvar detex-tex-right "" "TeX right group character used by detex.")
X
X(make-variable-buffer-local 'detex-tex-escape)
X(make-variable-buffer-local 'detex-re-tex-escape)
X(make-variable-buffer-local 'detex-not-tex-escape)
X(make-variable-buffer-local 'detex-latex-optional-left)
X(make-variable-buffer-local 'detex-tex-left)
X(make-variable-buffer-local 'detex-tex-right)
X
X(set-default 'detex-tex-escape "\\")
X(set-default 'detex-re-tex-escape "\\\\")
X(set-default 'detex-not-escape "[^\\\\]")
X(set-default 'detex-latex-optional-left "\\[")
X(set-default 'detex-tex-left "{")
X(set-default 'detex-tex-right "}")
X
X(defun detex-remove-latex-commands-with-garbage-argument ()
X  "Deletes latex-commands in detex-latex-commands-with-garbage-argument
Xtogether with their arguments."
X  ;;(message "detex-remove-latex-commands-with-garbage-argument...")
X  (mapcar 'detex-remove-latex-command-with-garbage-argument
X	  detex-latex-commands-with-garbage-argument))
X
X(defun detex-remove-tex-commands-with-garbage-argument ()
X  "Deletes tex-commands in detex-tex-commands-with-garbage-argument
Xtogether with their arguments."
X  (mapcar 'detex-remove-tex-command-with-garbage-argument
X	  detex-tex-commands-with-garbage-argument)) 
X
X(defun detex-remove-latex-command-with-garbage-argument (com)
X"Deletes latex-command COM together with its arguments."
X  (let ((command (concat detex-tex-escape com))
X	(re-command (concat detex-re-tex-escape com "\\b"))
X	b)
X    (goto-char (point-min))
X    (while (re-search-forward re-command (point-max) t)
X      (search-backward command)
X      (setq b (point))
X      (search-forward command)
X      (re-search-forward "[ \t]*")
X      (if (looking-at detex-latex-optional-left)
X	  (forward-sexp 1))
X      (re-search-forward "[ \t]*")
X      (if (looking-at detex-tex-left)
X	  (forward-sexp 1)
X	(forward-word 1))
X      (delete-region b (point)))))
X
X(defun detex-remove-tex-command-with-garbage-argument (com)
X"Deletes tex-command COM together with its arguments."
X  (let ((command (concat detex-tex-escape com))
X	(re-command (concat detex-re-tex-escape com "\\b"))
X	b)
X    (goto-char (point-min))
X    (while (re-search-forward re-command (point-max) t)
X      (search-backward command)
X      (setq b (point))
X      (search-forward command)
X      (re-search-forward "[ \t]*")
X      (if (looking-at detex-tex-left)
X	  (forward-sexp 1)
X	(forward-word 1))
X      (delete-region b (point)))))
X
X(defun detex-remove-latex-garbage-environments ()
X  "Deletes the latex environments in detex-latex-garbage-environments."
X  ;;(message "detex-remove-latex-garbage-environments")
X  (mapcar 'detex-remove-latex-environment detex-latex-garbage-environments))
X
X(defun detex-remove-latex-environment (env)
X  "Deletes latex environment ENV."
X  (goto-char (point-min))
X  (while (search-forward
X	  (concat detex-tex-escape "begin" detex-tex-left env) (point-max) t)
X    (search-backward detex-tex-escape)
X    (setq b (point))
X    (if (search-forward
X	 (concat detex-tex-escape "end" detex-tex-left env) (point-max) t)
X	(if (looking-at detex-tex-right)
X	    (delete-region b (1+ (point)))))))
X  
X(defun detex-remove-discretionary-hyphens ()
X  "Removes \\-."
X  ;;(message "detex-remove-hyphens...")
X  (goto-char (point-min))
X  (while (re-search-forward (concat detex-re-tex-escape "-") (point-max) t)
X    (replace-match "")))
X
X(defun detex-remove-display (&optional flavor)
X  "Deletes displays delimited by ""$$"".  If optional argument FLAVOR is 
X""latex"", then it also recognizes LaTeX displays."
X  (let (beg)
X    (goto-char (point-min))
X    ;;(message "detex-remove-display...")
X    (while (re-search-forward (concat detex-not-escape "\\$\\$") (point-max) t)
X      (setq beg (- (point) 2))
X      (if (re-search-forward (concat detex-not-escape "\\$\\$") (point-max) t)
X	  (delete-region beg (point))))
X    (if (equal flavor "latex")
X	(progn
X	  (goto-char (point-min))
X	  (while (search-forward (concat detex-tex-escape "[") (point-max) t)
X	    (setq beg (- (point) 2))
X	    (if (search-forward (concat detex-tex-escape "]") (point-max) t)
X		(delete-region beg (point))))))))
X
X(defun detex-remove-math (&optional flavor)
X  "Deletes math mode.  If optional argument FLAVOR is ""latex"", it also
Xrecognizes LaTeX constructions."
X  (let (beg)
X    ;;(message "detex-remove-math...")
X    (goto-char (point-min))
X    (while (re-search-forward (concat detex-not-escape "\\$") (point-max) t)
X      (setq beg (1- (point)))
X      (if (re-search-forward (concat detex-not-escape "\\$") (point-max) t)
X	  (delete-region beg (point))))
X    (if (equal flavor "latex")
X	(progn
X	  (goto-char (point-min))
X	  (while (search-forward (concat detex-tex-escape "(") (point-max) t)
X	    (setq beg (- (point) 2))
X	    (if (search-forward (concat detex-tex-escape ")") (point-max) t)
X		(delete-region beg (point))))))))
X
X(defun detex-remove-tex-commands ()
X  ;;(message "detex-remove-tex-commands")
X  (goto-char (point-min))
X  (while (re-search-forward
X	  (concat detex-re-tex-escape "[a-zA-Z]*") (point-max) t)
X    (replace-match "")))
X     
X(defun detex-remove-comments ()
X  ;;(message "detex-remove-comments...")
X  (goto-char (point-min))
X  (while (re-search-forward (concat detex-not-escape "%.*$") (point-max) t)
X    (replace-match "")))
X 
X(defun detex-remove-font ()
X  (goto-char (point-min))
X  (while (re-search-forward (concat detex-re-tex-escape "font[^=]* *= *\\w*")
X			    (point-max) t)
X    (replace-match "")))
X			    
X;;; If using a TeX/LaTeX dialect which uses other characters for 
X;;; escape and grouping, this function should be rewritten to
X;;; redefine TeX escape character etc, possibly mode-dependent.
X
X(defun define-escape-and-group ()	
X  )
X
X(defun delatex ()
X  (interactive)
X  (define-escape-and-group)
X  (detex-remove-latex-garbage-environments)
X  (detex-remove-display "latex")
X  (detex-remove-math "latex")
X  (detex-remove-comments)
X  (detex-remove-latex-commands-with-garbage-argument)
X  (detex-remove-discretionary-hyphens)
X  (detex-remove-tex-commands))
X
X(defun detex ()
X  (interactive)
X  (define-escape-and-group)
X  (detex-remove-display "tex")
X  (detex-remove-math "tex")
X  (detex-remove-comments)
X  (detex-remove-font)
X  (detex-remove-tex-commands-with-garbage-argument)
X  (detex-remove-discretionary-hyphens)
X  (detex-remove-tex-commands))
END_OF_detex.el
if test 7609 -ne `wc -c <detex.el`; then
    echo shar: \"detex.el\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0