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