[net.text] vi puzzle

gbergman@ucbtopaz.CC.Berkeley.ARPA (09/07/84)

One of the more fruitless things one can do in vi is to set up
abbreviations such as

	:ab yes yes!
or
	:ab O O'Shaughnessy

for as soon as you try to use the abbreviation, the editor goes into an
infinite loop.  Under 4.2bsd, ^C is usually successful in quickly
breaking the loop -- at least in regular text; on the bottom line the
results are less predictable.
     Problem:  suppose you have made such an abbreviation.  How can you
undo it without quitting the editor, or leaving vi mode?

     (To make this seem realistic, suppose that for some one-time
special piece of editing you are doing, you have set up some 50
different abbreviations over the course of several hours but not put
the commands in a file you can source -- so you don't want to quit the
editor and lose them all.  And suppose that you have mapped and ab'ed Q
and ^\  and that the ab's generate infinite loops as above, so you
can't unab or unmap them, and hence cannot go into ex mode.  Anyway,
even if you find some flaw in this scenario, the problem is still to
undo such an abbreviation while staying in vi mode -- which includes
the bottom line, of course.)

     Mail solutions to me, and in a couple of weeks I will summarize
those that I have received, along with the two that I have found
myself.  Please try them before sending them!  And mention what version
of the editor and unix you are using.  (If you're on 4.1 it may be hard
to break those infinite loops, so you might do best to experiment where
there is a second terminal at hand that you can use to kill the process,
or where you can do so by a hangup.  I don't know about other forms of
unix than 4.[12].)

			George Bergman
			Math, UC Berkeley 94720 USA
			...!ucbvax!ucbcartan!gbergman

gbergman@ucbtopaz.CC.Berkeley.ARPA (09/22/84)

The question was:  if you have set up a vi abbreviation which if
executed creates an infinite loop, such as

	:ab yes yes!
or
	:ab O O'Shaughnessy

how can you unabbreviate these, without leaving vi mode or killing
the editor?  (The reason that these create infinite loops is that
an abbreviation consisting of alphanumeric characters is expanded
when it is typed in vi insert mode preceded and followed by most any
nonalphanumeric characters.  In the above abbreviations, the expansion
begins with the abbreviation followed by a nonalphanumeric character,
and the expansion process is recursive, so this initial segment is again
expanded, and so on.  The difficulty with unabbreviating them is that
bottom-line commands in vi are processed like text-insertions; in
particular, abbreviations are expanded.  Since the "unabbreviate"
command-line is ended with a carriage-return or an escape, which
trigger expansion, an attempt to undo these abbreviations gives
the same infinite loop.)

     I know of two types solutions:

(1)  Create the desired command-line by trickery within the text,
then put it someplace from which it can be executed as a command.
     By what kind of trickery can the command line by created within
the text?  If you try typing "unab yess" and then backspace over the
last "s" and escape, the editor is smart enough to realize that you
have typed the word "yes", and will try to expand it.  But if you
escape after typing "unab yess" and then kill the last "s" with
the command "x", or if you type "unab ye" and escape, then append
an "s", or in any other way create it using two successive commands,
it will not count this as a candidate for expansion, and the desired
line can be achieved.
     To execute this line as a command, my method is to yank or
delete it to a named buffer, e.g. with the command
	"xdd
and the execute this buffer using
	:@x
Another way, pointed out by ihnp4!druxp!mab (Alan Bland) -- the only
person who submitted a solution to this "puzzle" -- is to write the
created command line to a temporary file
	:.w junk
and execute it with the source command
	:so junk

(2)  The other method is based on the fact that abbreviation may be
inhibited by typing ^V before the following character.  Generally
speaking, ^V prevents "special" interpretations of a character; in
this case it prevents a following nonalphanumeric character from
being interpreted as a trigger for abbreviation-expansion.
     This is not a solution in itself.  If you try typing
	:unab yes
and precede your closing carriage-return by a ^V, this will not only
prevent it from triggering the expansion, it will also prevent it from
executing the command-line; instead, you will get a ^M as part of the
command-line.  If you hit another carriage return, the line will be
executed, but since it now says
	:unab yes^M
and yes^M was never made an abbreviation, the editor will merely report
this fact.  Likewise, if you type :unab yes and then ^V followed by
a space and carriage return, you will get something that looks like
the desired line, but in fact it will have a space at the end, and the
editor will notice that yes-space was not abbreviated, and so report.
     However, there is one character which if put in an :unab
command line will not in general be interpreted as part of the
abbreviation.  This is |, which can be used to string bottom-line
commands together, e.g.
	:unab ucb|unab ucla|unab mit|unab hvd
Now if you type in the desired line  :unab yes,  followed by ^V  and
then by  |, and then any other command, for instance the vacuous
command, then your desired command :unab yes  will be entered and
executed.

     (You might wonder how one can put literal |'s into ab or
unab commands.  The answer is, by hitting ^V twice before
the |.  This prevents the special interpretation of ^V and allows
it to be included as an actual character in the command line, so that
at the next stage, where the command line is processed, it will prevent
interpretation of the | as a special symbol.  If in fact you wanted to
have the effect of a ^V appear when the abbreviation is expanded, you
need to hit it four times when executing the command line, while to
get a literal ^V in the expansion of an abbreviation, you would
have to hit it eight times, resulting in the appearance of four ^V's on
your command line, hence two within the abbreviation-output, of which
one prevents the special interpretation of the other when this output
is output.  As another example, suppose you are typing something up
to be lpr'd, and you wish to represent the empty set by its conventional
abbreviation, an overstruck O and /.  You decide to set up an
abbreviation whereby typing the word "es" will produce this effect.
The correct command-line turns out to be of the form
	:ab es O*/
where at the point marked *, you hit ^V either 5 or 7 times.  I leave
it to you to figure out why 5 or 7, but no other counts, will work!

     There are things about the :ab command that I still don't
understand.  For instance, I said that the expansion  of an abbreviation
will be triggered if it is "preceded and followed by "most any
nonalphanumeric character" but the actual situation is
more complicated.  Here are some examples that I have found by
trial and error.  Perhaps someone who can understand the source
code, or who has more patience testing out cases, can tell us the
general rule.  Of course, what works on the machines I use may not work
on yours!
	Let:
		s= space, tab, beginning/end of insert,
		a= alphanumeric
		*= most nonalphanumerics

				abbreviation works if
c o m m a n d		preceded by	&	followed by
			s   a   *		s  a  *
:ab ca California	+   -   +		+  -  +
:ab x  exact		+   -   -		+  -  +
:ab 's possessive	+   +   -		+  -  +
:ab can't cannot	never?
:ab & ampersand		never?

     Note that in all these cases, even those I have never been
able to make work, the abbreviation is "accepted" in the sense
that after the command has been given, the command
	:ab
will show the desired abbreviation among those stored.
     Have fun!

			George Bergman
			Math, UC Berkeley 94720 USA
			...!ucbvax!ucbcartan!gbergman