[net.emacs] word completion & buffer-specific dictionaries

podar@sbcs.UUCP (Sunil Podar) (06/24/85)

here it is folks: the word completion package. There is detailed 
documentation in the files below. I have included two dictionaries too,  
which for convenience sake I have named the dictionary files
differently; you will have to mv the dict.latex & dict.pascal files to
~/.dict.latex & ~/.dict.pascal ; the pascal dictionary file is just an 
experimental one and is not complete whereas the latex one is fairly complete.
You will have to create other mode dictionaries yourself.
Remember to remove any signature at the end of this file before running thru' 
sh. 
enjoy. any comments welcome.
------cut-here-including-this-line----cut-here-including-this-line--------
#!/bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	dictionary.ml
#	dict.latex
#	dict.pascal
# This archive created: Sun Jun 23 17:55:43 1985
export PATH; PATH=/bin:$PATH
echo shar: extracting "'dictionary.ml'" '(15491 characters)'
if test -f 'dictionary.ml'
then
	echo shar: over-writing existing file "'dictionary.ml'"
fi
cat << \SHAR_EOF > 'dictionary.ml'
; podar@sbcs (Sunil Podar) June 23, 1985 (SUNY at Stony Brook)
; any followups to:
; 	CSNET: 	podar@sbcs.csnet
; 	ARPA: 	podar%suny-sb.csnet@csnet-relay.arpa
; 	UUCP: 	{allegra, hocsd, philabs, ogcvax}!sbcs!podar
; 	mail:	Sunil Podar, Dept of Comp. Science
; 		SUNY at Stony Brook, N.Y. 11794
; 
; BUFFER/MODE-SPECIFIC DICTIONARIES AND WORD COMPLETION.
; ------------------------------------------------
; 1. fisrt set of functions: intext word completion
; 2. second set of functions at the end: "get-tty-..." emulation.
; 
; These functions provide intext word expansion similar to the
; command completion feature of emacs on buffer names &
; command-names. It is also similar to the abbrev mode but easier to
; use and slower. It uses a dictionary of words and helps by filling
; unique suffices of words being expanded.
; 
; The idea is to type a first few characters and type expansion-key
; (currently bound to ESC-space) and the suffix of the word to the
; left of dot is filled in, if any, and if there are choices then
; they are shown in a Help window. 
; 
; The  setup  here  is  fairly  general, although it has been written 
; primarily to help with typing long words  in  LaTeX.  This  package 
; provides the facility of having separate dictionaries for different 
; kinds of files i.e. it can be mode-dependent. The  words  on  which 
; such  expansion  will work are to be maintained in dictionary files 
; whose names must follow these conventions.
; 
; ~/.dict.<mode>[.local] 
; ~/.dict.global
; 
; <mode> can be any word you chose, e.g. latex, pascal, junk, c, etc.
; 
; E.g.  for  latex,  we  MUST  have a ~/.dict.latex and we MAY have a 
; ~/.dict.latex.local file, which if exists will also become part  of 
; the  dictionary.  The  ~/.dict.global  file  is  also  loaded if it 
; exists. The idea of such a setup is that .<mode>  files  will  have 
; the standard words, .<mode>.local can have any other words that are 
; used in that particular mode and one wants them expanded  too  such 
; as  long  variable  names, and this file can be purged from time to 
; time as the usage changes. The .dict.global is meant for words that 
; are  globally  used such as your name, university etc. .dict.global 
; file is always loaded into  each  dictionary  irrespective  of  the 
; mode.
; 
; The conventions used for dictionary can be easily changed to suit
; ones' need if there is any conflict or dislike for above. The
; function "setup-dictionary-expansion" below will have to be modified
; accordingly. All that is finally required by the setup here is that 
; there be ONE dictionary buffer associated with each buffer, and does
; not depend on how the dictionary files are maintained and how the 
; dictionary buffer is constructed.
; 
; A simple function "add-word-to-local-dictionary" is also provided
; that goes into recursive-edit and takes a region between mark & dot
; as the word to be added to the local dictionary. It also loads the 
; word into current dictionary buffer making it imediately available
; for expansion. The word thus added are added only to the ".....local"
; dictionary and it is your responsibility to make sure it is unique.
; You have to explicitly add words to the other ".<mode>" and 
; ".dict.global" dictionaries. The function can be executed interactively
; by typing "ESCx add-word-to-local-dictionary".
; 
; There are two ways to go about setting up this package.
; 1. EXPLICIT WAY:
;    From any buffer, type "ESCx load dictionary.ml", followed by
;    "ESCx setup-dictionary-expansion" which will ask you for name of
;    dictionary to which you must reply with the appropriate name of the
;    form "~/.dict.<mode>". The .....local & the .global dictionaries
;    are automatically loaded if they exist.
;    
; 2. AUTOMATED WAY: there are again two ways(!):
;    a. add this one line to EVERY <mode.ml> file that puts emacs in a
;    specific mode depending on the file pattern:
; 
;    	(setup-dictionary-expansion "~/.dict.<mode>") 
;           	where <mode>= pascal, latex, prolog, c, etc.
;    and add the following line in your .emacs_[pro|local] :
;    	(autoload "setup-dictionary-expansion" "...../dictionary.ml")
;    
;    b. If you do not wish to fiddle with the library mode files, then
;    you have to write a small defun for EACH mode and add them to
;    your .emacs_[pro|local] file, e.g.:
;    
;    	(autoload "setup-dictionary-expansion" "...../dictionary.ml")
;    	(auto-execute "latex-dictionary-expansion" "*.tex")
;    	(defun (latex-dictionary-expansion 
;              	   (setup-dictionary-expansion "~/.dict.latex")))
; 
;    	(auto-execute "pascal-dictionary-expansion" "*.p")
;    	(defun (pascal-dictionary-expansion 
;                  (setup-dictionary-expansion "~/.dict.pascal")))
;
; 3.  and  of course have your dictionaries setup beforehand. 
; 
; NOTE:  If  you  ever  manipulate  the  dictionaries  explicitely by 
; editing the files remember to make sure there are NO DUPLICATES. It 
; is  not  necessary  to have the files sorted, though one simple way 
; from within emacs is to enclose the whole buffer in a  region  (put 
; mark  at  end-of-file  and  go  to beginning-of-file) and then type 
; "ESCxfilter-region sort -u" and then a write-file.
; 
; Thats all folks, enjoy. Do let me know of any bugs or if you make
; any modifications or would like to change something and can't
; figure out how to. You are permitted to continue using the abbrev
; expansion feature of emacs although it is worth considering giving
; it up.
; PS:  This  program  ought  to  be written in C using grep/look etc.
; with an interface to emacs  or using the emacs C code  to  maintain
; tables just like for  "get-tty-buffer";  and  perhaps  a  different 
; dictionary organization along the lines of spell.  I do not know  C
; yet hence you are welcome to do so and please let me know if you do.
;------------------------------------------------------------------------

(declare-buffer-specific &dictionaryfile &dictionarybuffer)

(defun (setup-dictionary-expansion dictfile
	   (setq &dictionaryfile 
		 (arg 1 "name of file containing dictionary? "))
	   (setq &dictionarybuffer (substr &dictionaryfile -10 10))
	   (setq dictfile &dictionaryfile)
	   (save-excursion 
	       (temp-use-buffer &dictionarybuffer)
	       (if (= (buffer-size) 0)
		   (progn (setq needs-checkpointing 0)
			  (insert-file dictfile)
			  (if (file-exists "~/.dict.global")
			      (insert-file "~/.dict.global"))
			  (if (file-exists (concat dictfile ".local"))
			      (insert-file (concat dictfile ".local")))
		   )
	       )
	   )
	   (local-bind-to-key "intext-word-completion" "\^[ ") ; ESC-space
	   ; could use ^[^[ or ^\
       ))

; for add-word-to-dictionary see documentation above.
(defun (add-word-to-local-dictionary
	   (save-excursion word yesno dictfile dictbuffer
	       (setq dictfile &dictionaryfile)
	       (setq dictbuffer &dictionarybuffer)
	       (message "define a region between mark & dot defining"
		   " the dict. word, then type ^C")
	       (setq mode-line-format 
		     (concat "adding word to " dictfile ".local "
			     "(^C to define word & resume editing)"))
	       (recursive-edit)
	       (setq word (region-to-string))
	       (if (> (length word) 0)
		   (progn (message "add " """" word """" 
			      " to dictionary? (y/n)  ")
			  (setq yesno (char-to-string (get-tty-character))))
		   (setq yesno "n")
	       )
	       (setq mode-line-format default-mode-line-format)
	       (if (= yesno "y")
		   (progn 
			  (temp-use-buffer "*match-buf*")
			  (erase-buffer)
			  (insert-string (concat word "\n"))
			  (set-mark)(beginning-of-file)
			  (append-region-to-buffer dictbuffer)
			  (append-to-file (concat dictfile ".local"))
			  (message """" word """" " added to buffer & " 
			      (concat dictfile ".local"))
		   )
		   (message "Aborted.")
	       )
	   )
	   
       ))

; intext-word-completion picks up the word to the left of dot in the current
; buffer and calls the function "occurances-of-prefix" passing the picked-up-
; word as given prefix and fills in unique suffix in the text if one found.
; it complains (^G) if no word with the given word found, shows choices in
; a Help buffer if more than one choice. If a suffix (unique or not) of
; >= one character is found then it is filled in.
; NOTE: since just one word to the left of dot is picked up the expansion
; will work only with that word as the prefix hence no spaces allowed in
; the prefix that you have when you type ESC-space. The dictionary can of
; course have any thing; for example if a dictionary word looked like:
; program main (input, output);
; then in the text buffer any prefix of the word "program" will be expanded
; but not if the scenario was "program m<ESC >" since only the "m" will be
; picked up.
(defun (intext-word-completion fullword
	   (set-mark)
	   (backward-word)
	   (exchange-dot-and-mark)
	   (setq fullword (occurances-of-prefix (region-to-string)))
	   (if (= fullword "\^G") 
	       (error-message "no word in dictionary with the given prefix.")
	       (= fullword (region-to-string)) (display-choice)
	       (progn (if (= (substr fullword 1 1) "\^G")
			  (setq fullword (substr fullword 2 -1)))
		      (delete-to-killbuffer)
		      (insert-string fullword)
	       )
	   )
       ))

; occurances-of-prefix essentially greps all the words with the given prefix
; and puts them into *match-buf* and returns info about the results:
; it returns "^G" if no word with the given prefix found,
;            (concat "^G" word) if a unique word with given prefix is found,
; 				the ^G is not really needed here but I needed
; 				it for "get-tty-dictionary-word" below.
; 				It is stripped away by the function
; 				"intext-word-completion" above.
; 	     largest-common-prefix from the words that matched the given
; 				prefix. If no additional characters are found
; 				then the choices are shown in a
; 				poped-up-buffer.
; PS: could use fast-filter-region and grep ^givenprefix on dictionarybuffer
; of course after copying it into *match-buf* or directly on the files.
(defun (occurances-of-prefix givenprefix1 i firstword
	   (setq givenprefix1 (arg 1))
	   (save-excursion 
	       (setq i 0)
	       (temp-use-buffer "*match-buf*")
	       (setq needs-checkpointing 0)
	       (erase-buffer)
	   )
	   (save-excursion 
	       (temp-use-buffer &dictionarybuffer)
	       (beginning-of-file)
	       (while (! (error-occurred 
			     (re-search-forward (concat "^" givenprefix1))))
		      (setq i (+ i 1))
		      (if (= i 1) (progn (beginning-of-line)
					 (set-mark)
					 (end-of-line)
					 (setq firstword (region-to-string)))
		      )
		      (beginning-of-line)
		      (set-mark)(end-of-line)(forward-character)
		      (append-region-to-buffer "*match-buf*")
	       )
	       (if (= i 0) "\^G"
		   (= i 1) (concat "\^G" firstword)  ; ^G => unique, klugy way
		   (largest-common-prefix givenprefix1)
	       )
	   )
       ))

; largest-common-prefix works on *match-buf* buffer where it expects strings
; one per line which have a given common prefix. These are picked out from
; the dictionary buffer. It returns the largest common prefix among all those
; strings (a "" if that's the case).
(defun (largest-common-prefix n lcprefix 
	   (save-excursion 
	       (temp-use-buffer "*match-buf*")
	       (beginning-of-file)
	       (set-mark)(end-of-line)
	       (setq lcprefix (region-to-string))
	       (next-line)
	       (while (& (!= lcprefix (arg 1)) (! (eobp)))
		      (setq n (+ (length (arg 1)) 1))
		      (beginning-of-line)
		      (set-mark)(end-of-line)
		      (while (= (substr lcprefix 1 n)
				(substr (region-to-string) 1 n))
			     (setq n (+ n 1))
		      )
		      (setq lcprefix (substr lcprefix 1 (- n 1)))
		      (next-line)
	       )
	   )
	   lcprefix 
       ))

(defun (display-choice
	   (save-excursion
	       (pop-to-buffer "Help")
	       (setq needs-checkpointing 0)
	       (erase-buffer)
	       (insert-string "Choose one of the following:\n")
	       (yank-buffer "*match-buf*")
	       (beginning-of-file)
	   )
       ))

;----------------------------------------------------------------------
; "get-tty-...." emulation for words from dictionary. This is a separate
; set of functions and not needed for intext-word-completion. The emulation
; also differs in its aim: here the aim is to insert the completed word in 
; the buffer.
;-----------------------------------------------------------------------
(local-bind-to-key "get-tty-dictionary-word" "\^\")

(defun (get-tty-dictionary-word
	   (help-word-completion "dictionary-word: ")
       ))

; if "get-tty-dictionary-word" is to be bound to a regular key, such as \
; for LaTeX, then we will have to arrest all the chars such as ;,><=....
; that immediately follow the \ since they are not in the dictionary; 
; which means have a separate function such as the following (perhaps not
; exactly) which will also be responsible for inserting the \ in text;  the
; dictionary does not have \ at the beginning of words.
; (defun (dictionary-word-arrest-non-alpbabets char prompt
; 	   (message (arg 1))
; 	   (setq char (get-tty-character))
; 	   (if (| (& (>= char 48)(<= char 57))    ; 0..9     these ranges are
; 		  (& (>= char 64)(<= char 90))    ; @A..Z    not perfect.
; 		  (& (>= char 97)(<= char 122))   ; a..z
; 	       )
; 	       (progn (push-back-character char)
; 		      (help-word-completion (arg 1)))
; 	       (= char 7) 	; ^G
; 	       (error-message "Aborted.")
; 	       (insert-character char)
; 	   )
; ))
; 
; The following function emulates "get-tty-..." command with all the bells &
; whistles and helps you with completion of words from a dictionary.The only
; difference here is that since "get-tty-character" is used to get chars, the
; cursor remains in the buffer area while the word completion is going on.
; couldn't get recursion working because of local variables, hence had to
; use the dirty way using "done".
; 
(defun (help-word-completion char givenprefix filledword done prompt
	   (setq prompt (arg 1))
	   (setq givenprefix "")
	   (setq done 0)
	   (while (!= done 1)
	       (setq char 0)
	       (message (concat prompt givenprefix))
	       (while (& (!= char 32)(!= char 63)(!= char 7)(!= char 13))
		      (setq char (get-tty-character))
		      (if (| (= char 8) (= char 127))
			  (setq givenprefix (substr givenprefix 1 -1))
			  (setq givenprefix 
				(concat givenprefix (char-to-string char)))
		      )
		      (message (concat prompt givenprefix))
	       )
	       (setq givenprefix (substr givenprefix 1 -1)) ; to get rid of 
	       ; 32/63/7/13 char
	       (if (= char 7) 
		   (error-message "Aborted.")
		   (progn (setq filledword (occurances-of-prefix givenprefix))
			  (if (= char 63) 
			      (display-choice)
			      (= filledword "\^G")  ; no word with given prefix
			      (send-string-to-terminal "\^G")
			      (= (substr filledword 1 1) "\^G") ; uniq string found
			      (progn (insert-string (substr filledword 2 -1))
				     (message "")
				     (setq done 1)
			      )
			      (> (length filledword) (length givenprefix))
			      (setq givenprefix filledword)
			      (= char 13)  ; ^M & prefix is a valid word
			      (progn (insert-string givenprefix)
				     (message "")
				     (setq done 1)
			      )
			      (display-choice) ; pop show choices
			  )
		   )
	       )
	   ) ; while not done
       ))

SHAR_EOF
if test 15491 -ne "`wc -c 'dictionary.ml'`"
then
	echo shar: error transmitting "'dictionary.ml'" '(should have been 15491 characters)'
fi
echo shar: extracting "'dict.latex'" '(3646 characters)'
if test -f 'dict.latex'
then
	echo shar: over-writing existing file "'dict.latex'"
fi
cat << \SHAR_EOF > 'dict.latex'
Alph
Downarrow
Huge
LARGE
Large
Leftarrow
Leftrightarrow
Longleftarrow
Longleftrightarrow
Longrightarrow
Rightarrow
Roman
Uparrow
Updownarrow
acute
addtocontents
addtocontentsline
addtocounter
addtolength
aleph
amalg
approx
arabic
arccos
arcsin
arctan
arg
arraycolsep
arrayrulewidth
arraystretch
ast
asymp
backslash
baselineskip
baselinestretch
begin
beginlistoffigures
beginlistoftables
begintableofcontents
bibitem
bigcap
bigcirc
bigcup
bigdot
bigoplus
bigotimes
bigskip
bigskipamount
bigsqcup
bigtriangledown
bigtriangleup
biguplus
bigvee
bigwedge
blackandwhite
boldmath
bot
bottomfraction
bowtie
breve
cal
cap
caption
cdot
cdots
chapter
check
circ
circle
circle*
cleardoublepage
clearpage
clubsuit
colorslides
columnsep
columnseprule
columnwidth
cong
contentsline
coprod
copyrightspace
cos
cosh
coth
csc
cup
dashbox
dashv
dblfloatpagefraction
dblfloatsep
dbltextfloatsep
ddagger
ddot
deg
det
diamondsuit
dim
displaymath
displaystyle
div
documentstyle
doteq
dotfill
doublerulesep
downarrow
ell
em
emptyset
end
epsilon
eqnarray
equiv
evensidemargin
exp
fbox
fboxrule
fboxsep
fleqn
floatpagefraction
floatsep
flushbottom
flushleft
flushright
fnsymbol
fontsize
footheight
footnotemark
footnotesep
footnotesize
footnotetext
footskip
forall
frac
framebox
gcd
geq
glossaryentry
grave
hat
hbar
headheight
headsep
heartsuit
hfill
hline
hoare
hom
hookleftarrow
hookrightarrow
hrulefill
hspace
huge
imath
include
includeonly
indexentry
indexspace
inf
infty
int
intextsep
it
itemindent
itemsep
jmath
ker
kill
label
labelwidth
large
lceil
ldots
leadsto
leftarrow
leftharpoondown
leftharpoonup
leftmargin
leftmargini
leftmarginii
leftmarginiii
leftmarginiv
leftmarginv
leftmarginvi
leftrightarrow
leftrightharpoons
leq
leqno
lfloor
lhd
lim
liminf
limsup
line
linebreak
linethickness
linewidth
listoffigures
listoftables
listparindent
log
longleftarrow
longleftrightarrow
longmapsto
longrightarrow
makebox
makeglossary
makeindex
makelabels
maketitle
mapsto
marginpar
marginparpush
marginparsep
marginparwidth
mathindent
max
mbox
medskip
medskipamount
mho
mid
min
minipage
mit
multicolumn
multiput
nabla
nearrow
neg
newcommand
newcounter
newenvironment
newlength
newline
newpage
newsavebox
newtheorem
nofiles
noindent
nolinebreak
nopagebreak
normalmarginpar
normalsize
nwarrow
obeycr
oddsidemargin
odot
oint
ominus
onecolumn
onlynotes
onlyslides
oplus
oslash
otimes
oval
overbrace
overline
pagebreak
pagenumbering
pageref
pagestyle
parallel
parbox
parindent
parsep
parskip
parstretch
partial
partopsep
perp
phi
poptabs
prec
preceq
prod
propto
pushtabs
put
raggedbottom
raggedright
raisebox
rangle
rceil
ref
renewcommand
renewenvironment
restorecr
reversemarginpar
rfloor
rhd
rho
rightarrow
rightharpoondown
rightharpoonup
rightmargin
rm
savebox
sbox
sc
scriptscriptsize
scriptscriptstyle
scriptsize
scriptstyle
searrow
sec
section
setcounter
setlength
setminus
settowidth
sf
shortstack
sigma
sim
simeq
sin
sinh
sl
small
smallskip
smallskipamount
spadesuit
sqcap
sqcup
sqrt
sqsubset
sqsubseteq
sqsupset
sqsupseteq
stackrel
subitem
subsection
subseteq
subsubitem
subsubsection
succ
succeq
sup
supset
supseteq
surd
swarrow
tabbingsep
tabcolsep
tableofcontents
tan
tanh
textfloatsep
textfraction
textheight
textstyle
textwidth
thebibliography
thechapter
theindex
thesection
theta
thicklines
thinlines
thispagestyle
tilde
tiny
titlepage
tocdepth
topfraction
topmargin
topnumber
topsep
totalnumber
triangleleft
triangleright
tt
twocolumn
twoside
typein
typeout
unboldmath
underbrace
underline
unitlength
unlhd
unrhd
uparrow
updownarrow
uplus
usebox
usecounter
varepsilon
varphi
varrho
varsigma
vartheta
vdash
vec
vector
vee
verbatim
vline
vspace
widehat
widetilde
SHAR_EOF
if test 3646 -ne "`wc -c 'dict.latex'`"
then
	echo shar: error transmitting "'dict.latex'" '(should have been 3646 characters)'
fi
echo shar: extracting "'dict.pascal'" '(103 characters)'
if test -f 'dict.pascal'
then
	echo shar: over-writing existing file "'dict.pascal'"
fi
cat << \SHAR_EOF > 'dict.pascal'
begin
const
end
function
procedure
program main (input, output);
type
var
reset
rewrite
readln
writeln
SHAR_EOF
if test 103 -ne "`wc -c 'dict.pascal'`"
then
	echo shar: error transmitting "'dict.pascal'" '(should have been 103 characters)'
fi
#	End of shell archive
exit 0
-- 
Sunil Podar
SUNY at Stony Brook

	CSNET: podar@sbcs.csnet
	ARPA: podar%suny-sb.csnet@csnet-relay.arpa
	UUCP: {allegra, hocsd, philabs, ogcvax} !sbcs!podar