maart@cs.vu.nl (Maarten Litmaath) (08/02/89)
: This is a shar archive. Extract with sh, not csh. : This archive ends with exit, so do not worry about trailing junk. : --------------------------- cut here -------------------------- PATH=/bin:/usr/bin:/usr/ucb echo Extracting 'macros' sed 's/^X//' > 'macros' << '+ END-OF-FILE ''macros' XVi Macros, Abbreviations, and Buffers XCopyright (C) 1988 by Fred Buck; all rights reserved. XAdditions: 1989 Maarten Litmaath <maart@cs.vu.nl> XThanks to: Jean-Pierre Radley <jpradley!root@uunet.uu.net> X XHeading summary: X================= X X Introduction X X Non-macro stuff you really should know about when writing macros X X Text markers X Text buffers X The escape filter X X Simple description of the various 'vi' macro mechanisms X X Text abbreviation X Keystroke remapping: text mode ("map!") X Keystroke remapping: command mode ("map") X Text-buffer execution X X More detail about 'vi' macro operation X X Mode-bouncing X Chained macros X Recursive macros X Terminating a recursive macro X X Peculiar limitations and restrictions on 'vi' macros X X Size X Putting and yanking to/from named buffers X Which keys to remap? X X Conclusion X XIntroduction X============= X X'Vi' offers a variety of "macro" facilities; a "macro" in this context is a Xmechanism whereby a small number of keystrokes, usually a single keystroke, Xis made to represent another set of keystrokes, so that typing the first set Xof keystrokes is equivalent to typing the second set. Applications for Xmacros fall into three major classes: X X (a) during text entry, allowing a long, often-repeated X string of text to be represented by a short abbreviation; for X instance, in your biography of Ramaswamy Gopalikrishnan, you X might assign "rgn" as a macro for the name of your subject; X X (b) existing 'vi' commands can be made invokable by X keys other than the traditional ones, so that for instance if X your terminal has function keys, you can remap the sequences X generated by the function keys to existing 'vi' commands, and X thereafter use the function keys instead of the standard 'vi' X command keys; X X (c) complex operations that would take many keystrokes, X or that would take a hard-to-remember sequence of keystrokes, X can be tied to an easy-to-remember sequence of keystrokes, and X be executed quickly and easily. X XIf your primary interest lies in category (c), then 'vi' macros should be the XLAST topic you take up when learning about 'vi', because you can't write Xmacros to accomplish things you couldn't do yourself if you were typing Xeverything in at the keyboard: your macros are only as good as your overall Xskill at using 'vi'. If you're not familiar with using the named and unnamed X'vi' text buffers, or with the use of text markers, or with the shell-escape X("!") filtration mechanism, the next section contains a bare-bones Xintroduction to these things, but you should really play with them at some Xlength yourself in order to see their full possibilities. X X XNon-macro stuff you really should know about when writing macros X================================================================= X XThe following is only a very cursory summary of some basic 'vi' mechanisms Xthat are essential to be familiar with when writing complex 'vi' macros. XThese summaries are intended to make at least intelligible the discussion Xthat is to follow them. If you don't know about these mechanisms, and if Xyou want to write complex 'vi' macros, you'd be well-advised to seek out Xa text on 'vi' read up on these things. X XText Markers: X-------------- X X'Vi' has 26 available text markers, corresponding to the 26 lowercase letters Xof the alphabet. A marker can be emplaced by moving the cursor to the place Xyou want to mark, and typing "m" followed by the marker name you want to use, Xfrom "a" to "z". Typing "ma" marks that location with marker "a". To return Xto this location from somewhere else in the text, type "`a", where "`" is a Xbackquote sign, or grave accent. The backquote moves the cursor to the Xprecise location you were at when you emplaced the mark. If instead you're Xsatisfied merely to go to the beginning of the line you were on when you Xemplaced the mark, type "'a", where "'" is a standard single-quote sign. X X A text region can often be defined by a cursor-movement command, and Xthe single-quote and back-quote commands are cursor-movement commands. XPlacing text markers makes it much more easy to yank text into a buffer. X XText buffers: X-------------- X X'Vi' has a remarkable total of 36 user-reachable text buffers. 26 of these Xare static buffers, meaning that they don't change until the user tells them Xto change, and these are called the "named" text buffers, with names Xcorresponding to the 26 letters of the alphabet. Don't confuse these "named" Xbuffers with the 26 text-marker names; the two are entirely separate and Xindependent. The text markers must always be in lowercase; the text-buffer Xnames can be either uppercase or lowercase, although whether a text-buffer Xname is in uppercase or in lowercase changes the way text gets loaded into Xthe buffer. X X To put text into a named buffer, type a double-quotation mark, then Xthe name of the buffer, and then a cursor-movement command, like this: X X "ay`b X Xwhich yanks into named text buffer "a" the text between your current cursor Xposition and the exact location of text marker "b", which we assume you've Xpreviously set. The "yank" will end JUST BEFORE the exact location of text Xmarker "b". To yank simply an entire line, use "yy" (or "Y") instead of "y" Xand the cursor-movement command; to yank N number of lines from your current Xcursor position, use X X "aNyy X XThe double-quote mark, '"', is the 'vi' command character that indicates that Xyou want to manipulate a text buffer. X X If you use an uppercase rather than a lowercase letter for the name Xof the text buffer, then whatever you yank into the buffer will be appended Xto the buffer, rather than over-writing the buffer's previous contents. X X To re-insert text from a text buffer, position the cursor where you Xwant to make the insertion, and type a double-quote sign, then the name of Xthe buffer (case doesn't matter for re-insertion), and then either "p" or X"P": X X "ap X XIf you use a lowercase "p", then the text is re-inserted after your current Xcursor position; if you use an uppercase "P", then the text is re-inserted Xbefore your current cursor position. X X In addition to the 26 named text buffers, 'vi' stacks deleted text Xin a set of 10 volatile buffers, generally called the "unnamed" buffers. By X"volatile" I mean that the contents of these buffers change without the Xuser's explicit direction. Actually, only the first of these volatile, Xdelete buffers is really "unnamed"; it's the buffer that the most-recently- Xdeleted text resides in, and from which the bare "p" and "P" commands work. XThe other nine delete buffers are reachable with the names "1" through "9". XSo if you delete some text in 'vi', the text you've just deleted is in the X"unnamed" delete buffer and can be recovered with "p" or "P". If, without Xrecovering that text, you now delete some more text, then the text you Xpreviously deleted is moved to delete buffer "1" and the most-recently-deleted Xtext is now in the "unnamed" delete buffer. Keep deleting text, and the text Xyou deleted the first time will advance from buffer "1" to buffer "2" and so Xon, until it gets to buffer "9", and if you delete more text after that, Xwhatever is in buffer "9" is thrown away, and can no longer be recovered. XActually it can be recovered if you haven't written the file since you deleted Xthe text: X :w temp X :e! X XThe first command writes the current (modified) contents of the file you're Xediting to a temporary file called 'temp', the second command tells 'vi' to Xreload the original file WITHOUT saving the modifications you've made. XThey are, however, still available in 'temp'. X X The syntax for working with the delete buffers is the same as that Xfor the named buffers: to extract text from, say, delete buffer "1", you'd Xsay X X "1p X XThe "unnamed" delete buffer, in which the most-recently-deleted text is Xstored, is recoverable simply by typing a "p" or a "P". X XThe escape filter X------------------ X XAn arbitrary set of text lines can be sent through a Unix or Xenix command Xand the result substituted for these lines in place. This is done within X'vi' by the command X X !<cursor-movement-command><Unix/Xenix command> X Xwhich has the effect of sending the text lines beginning with the current Xline and ending with the line your cursor-movement-command has taken you to. XIf in 'vi' the cursor were placed at the beginning of this paragraph, "An Xarbitrary", and then the command X X !/has the effect/<RETURN>tr '[a-z]' '[A-Z]<RETURN> X Xwere entered, every alphabetic character from the cursor position to the end Xof the line containing the string "has the effect" would be forced to Xuppercase. Note that most versions of 'vi' operate only in whole-line Xincrements when using the shell-escape filter; some versions of 'vi' allow Xthe shell-escape filter to work on text areas that don't correspond exactly Xto line boundaries. X XSimple description of the various 'vi' macro mechanisms X======================================================== X X (1) text abbreviation, which operates only in text-entry mode (the X "abbr" ex-escape command); once set, an abbreviation works X only in "vi" text-entry mode; X X (2) keystroke remapping, which can operate either in text-entry X or in command mode (the "map!" and "map" ex-escape commands); X once set, a "map!"'ed sequence is triggered only in text-entry X mode, and a "map"'ed sequence is triggered only in "vi" X command mode; X X (3) text-buffer execution, which operates only in command mode: X once text has been placed in any of the named text buffers, X that text can be executed as if it were a sequence of 'vi' X commands. X XText abbreviation X------------------ X XUsing macros during text-entry is almost always motivated by the goal of just Xsaving keystrokes. The text-abbreviation macro in 'vi' is set up by going to X"ex-escape" mode by typing a colon, and has the form X X abbr <abbreviation> <expansion> X Xwhere an example might be X X abbr gatt General Agreement on Tariffs and Trade X X[Note that the name of the text abbreviation cannot contain an embedded Xspace. This is a limitation of all the 'vi' macro mechanisms except for text- Xbuffer execution, which uses the names of the named text buffers to define Xwhich macro to execute; a buffer name is a single alphabetic letter.] X X Once this is done, then while in text-entry mode, whenever you type Xa non-alphanumeric character followed by the string "gatt", 'vi' will examine Xthe next character you type to see if it's non-alphanumeric, and if so, then X"gatt" will be erased and "General Agreement on Tariffs and Trade" will be Xsubstituted for it. (Typing "gatt" at the very top of your text qualifies as Xa non-alphanumeric character followed by "gatt".) X X If you don't want a particular instance of "gatt" to get converted, Xeven though it's preceded and followed by a non-alphanumeric character, you Xhave to escape the first character following "gatt" by typing X X ^V X X(that is control-V), so if you want "gatt!" to appear, you type X X gatt^V! X X With text abbreviations, your text appears as you type it, and no Xtransformation is performed on your specified abbreviation until you follow Xit with a non-alphabetic character (which includes an immediate exit from Xtext-entry mode). This differentiates text abbreviation from text-mode Xkeystroke-remapping using the "map!" command, which will be discussed soon. X X Text abbreviations can be canceled with the ex-escape "unabbr" Xcommand, X X unabbr gatt X XMost simple abbreviations can be unabbreviated via a simple ex-escape Xcommand, the kind you introduce by typing a colon from 'vi' command mode. XBut many cannot, especially mode-bouncing abbreviations, to be discussed Xlater. For these, you must enter genuine "ex" mode by typing a capital Q X("Q") from 'vi' command mode. Then do your "unabbr gatt". To return to "vi" Xmode, enter "vi" or "visual" at the "ex" colon prompt. X XYou can get a list of your currently active abbreviations by entering simply X"abbr" in ex-escape mode. X XKeystroke remapping: text mode ("map!") X---------------------------------------- X XAs previously mentioned, the text abbreviation mechanism won't make a Xsubstitution on text that you type until you type a character AFTER the Xabbreviation that persuades 'vi' that you want 'vi' to expand the Xabbreviation. Also, 'vi' echoes each character of the abbreviation as you Xtype it, just in case you really want the "abbreviation" to be a literal Xsequence of characters in your text. So with the previous example, if you're Xtyping away and enter X X "I also want to introduce a new word, 'gattblather'..." X Xthen if you have "gatt" abbreviated for "General Agreement on Tariffs and XTrade", your text line will remain as-is, with no substitution for the string X"gatt" in "gattblather". Furthermore, an abbreviated sequence must be preceded Xas well as followed by a non-alphanumeric character for the substitution to be Xtriggered. X X Keystroke-remapping works a bit differently. Keystroke-remapping is Xhandled by the ex-escape "map" command, which takes two forms: "map" operates Xon characters that are typed in command mode, and "map!" operates on Xcharacters that are typed in text-entry mode. (Some versions of 'vi' may Xreverse this.) X X Staying with the previous example, the ex-escape command X X map! gatt General Agreement on Tariffs and Trade X Xcauses the key-sequence "gatt" to be instantly replaced by "General Agreement Xon Tariffs and Trade" wherever it's typed. The abbreviation mechanism waits Xto see what the NEXT character after the abbreviation is going to be, before Xdeciding whether to make a substitution, and if the sequence wasn't preceded Xby a non-alphanumeric character (or by the top of the file), the abbreviation Xmechanism doesn't start working at all. X XBy contrast, the keystroke-remapping mechanism instead starts a per-character Xtimer that begins whenever you type the first character of a remapped Xsequence. When you type "g", nothing will be echoed to your screen unless Xyou type some character other than "a", or unless you type nothing at all Xwithin a timeout period, typically about two seconds long. In short, 'vi' Xwatches to see if you've typed the beginning of a remapped sequence, and if Xthe sequence is longer than a single character, 'vi' waits to see if you'll Xtype the complete sequence, in which case 'vi' will supply whatever remapping Xyou've specified for the sequence you've just typed. This will happen Xregardless of what follows the remapped sequence. So if you've remapped X"gatt" to "General Agreement on Tariffs and Trade", then entering X X "I also want to introduce a new word, 'gattblather'..." X Xin text mode will yield X X "I also want to introduce a new word, 'General Agreement X on Tariffs and Tradeblather'..." X XAs with abbreviations "^V" will let you escape the mapping, but this time Xthe macro has to be preceded rather than followed by it. The following will Xget you a plain "gatt", regardless of how fast you type it: X X ^Vgatt X XTo un-map! a previously map!'ed text-mode sequence like "gatt", use the ex- Xescape command X X unmap! gatt X XMost simple map!'ings can be un-map!'ed via a simple ex-escape command, the Xkind you introduce by typing a colon from 'vi' command mode. But many Xcannot, especially mode-bouncing map!'ings, to be discussed later. For Xthese, you must enter genuine "ex" mode by typing a capital Q ("Q") from 'vi' Xcommand mode. Then do your "unmap! gatt". To return to "vi" mode, enter X"vi" or "visual" at the "ex" colon prompt. X XYou can get a list of your currently active text-mode remapped sequences by Xentering merely "map!" in ex-escape mode. X XNEVER MAKE A map!'ed DEFINITION RECURSIVE! More on this later. X X XKeystroke remapping: command mode ("map") X------------------------------------------ X XSequences that you've made into text abbreviations, or that you've remapped Xusing "map!", are triggered only when you're in text-entry mode, and are not Xtriggered in command mode. (Note, however, that an ex-escape command line is Xconsidered by 'vi' to constitute text-entry mode.) Command-mode remapping is Xdone with the "map" ex-escape command -- note the missing exclamation point X-- and works identically with text-entry remapping except that "map"'ed Xsequences are triggered only in command mode and are not triggered in text- Xentry mode. X X Command-mode keystroke remappings can be canceled the same way text- Xentry mode keystroke remappings can, except without the exclamation point: X X unmap ; X XSome 'vi' command keys simply cannot be remapped in command mode; the set of Xthese keys varies according to the implementation of 'vi'. The most common Xunremappable keys are ":" and "u". X XYou can get a list of your currently active command-mode remapped sequences Xby entering merely "map" in ex-escape mode. X X XText-buffer execution X---------------------- X XThis topic, which is often glossed over or omitted entirely in most Xproprietary 'vi' documentation, can be stated very simply: any named text Xbuffer can be treated as a 'vi' command-mode macro by typing the at-sign Xcharacter ("@") followed by the name of the buffer. If named buffer "a" Xcontains X X H!Lsort X Xthen "@a" from 'vi' command mode will sort the lines now on the terminal Xscreen. The same would of course be true of a "map" that said the same Xthing; this example is for illustration. X X The ability to execute a named text buffer as if the buffer were a Xsequence of commands allows 'vi' macros sometimes to operate in a self- Xmodifying way, because a macro can load a text buffer from text in the Xcurrent file and then invoke that text buffer as a command macro, without Xhaving to know in advance what the text is that will be loaded into the Xbuffer. X XMore detail about 'vi' macro operation X======================================= X XMode-bouncing X-------------- X XThe various 'vi' macro facilities differ in whether they're triggered in text- Xentry or in command mode. Text-abbreviation is triggered only in text-entry Xmode; the "map!" command creates macros that are triggered only in text-entry Xmode; the "map" command creates macros that are triggered only in command Xmode; and the "@" command works only in command mode. X X But all this just means that the >trigger< for the macro must come Xwithin the mode that the macro corresponds to. Any macro, regardless of Xtype, can switch between the invocation mode and other modes and back again. XThis is what's meant by a "mode-bouncing" macro. Any 'vi' macro can go Xfrom text-entry to command mode, to ex-escape mode, or to vi-mode. You just Xdefine the macro with the exact keystroke sequence that you yourself would Xuse if you were changing modes. I refer to macros that shift from one mode Xto another as "mode-bouncing" macros. X X Control characters are entered into macro definitions by preceding Xthem with a control-V. The result, after the control character is typed, Xappears as something like "^M" for a carriage return, or "^[" for an ESCAPE. XSay for the sake of argument you've abbreviated "dater" this way: X X abbr dater ^M^[:.!date +\%D^MkJA X Xwhere "^M" stands for the keystroke sequence "control-V" followed by "control- XM", and "^[" stands for "control-V" "<ESCAPE>". Try jotting this down on a Xpiece of paper, or printing it, and then trying the definition out by typing Xeach keystroke manually. Then try defining the abbreviation with the ex- Xescape "abbr" command, going into text entry mode, and typing text that Xcontains the string "dater" preceded and followed by a non-alphanumeric Xcharacter, say, X X I wonder if today is dater? X XNote that strange things might happen if instead of an abbreviation, you Xused "map!" to make this macro, and then made a minor spelling error in Xthe middle of some other sentence and said "validater". This difference Xis the reason that 'vi' retains both "abbr" and "map!". Even with X"abbr", it's possible to collide with some valid use of "dater", as in X"Please take this form and run it through the dater;" the lesson here is, Xthe main problem with 'vi' text macros is pilot error, the failure to Xanticipate future collisions that will have unintended effects. X X[Sidenote: the reason that '%' is preceded by a backslash in the above Xexample is that on the "ex" command line, '%' is a magic character standing Xfor the name of the file currently being edited, and therefore must be Xescaped to avoid this special meaning. The two most common other Xcharacters that have special significance on the "ex" command line are X"#", which stands for the last file previously edited, and "!", which Xstands either for the shell-escape, shell-filter, or command-history Xmechanisms depending on its position within the line. All of these Xcharacters should be escaped on the "ex" command line when you don't Xwant "ex" to see them specially.] X XText-entry macros that bounce between modes are notoriously difficult to Xcancel ("unabbr" or "unmap!") with a simple ex-escape command, the kind you Xintroduce by typing a colon in 'vi' command mode. This is because 'vi' Xbelieves that a simple ex-escape command line is being typed in text-entry Xmode, therefore it expands the name of the abbreviation or remap!'ing as you Xenter it. The only effective way to get rid of such macros (and probably you Xshould make it your habit to use this method to unmap ALL macros, just so you Xdon't have to risk making a mistake evaluating which macro should be erased Xthis way and which should not) is to type a capital Q ("Q") in 'vi' command Xmode, which puts you into genuine "ex" mode. From now on, 'vi' can't see Xyou. Do your "unabbr" or "unmap!", then enter "vi" or "visual" at the next Xcolon prompt to return to 'vi'. X XChained macros X--------------- X XMacros, being merely pre-defined sequences of keystrokes, can invoke other Xmacros, anywhere in their definition. Such "sub-macros" operate like Xsubroutines in a computer program. Take the following example: X X map = :set wm=3^M X map ; ^[=oThe End^[O X XWhat this does is to first enter 'vi' command mode, in case we're not already Xthere; invoke the "=" macro to set word-wrap with a hotzone of 3 characters, Xopen text for entry, insert the words "The End" on a line by themselves, exit Xtext-entry mode, open a new line above "The End", and let the user start Xtyping in whatever text is to be ended by "The End". X XThis can be done at any number of levels; the "=" macro could itself call Xother macros, and so forth. X X XRecursive macros X----------------- X XA macro that invokes itself is called a "recursive" macro. 'Vi' tries to Xlimit the creation of recursive macros by prohibiting macros that embody X"tail recursion", which is to say, a macro that ends by invoking itself. XThis policing system is only partial. Nothing stops you from imbedding a Xrecursive macro call INSIDE (as opposed to at the end of) a macro definition. XAlthough 'vi' will object to a definition like X X map! $ 123456$ X Xit'll accept X X map! $ 123$456 X XTHIS IS AN ABSOLUTE DISASTER. You probably wouldn't make this mistake Xyourself, but you MIGHT define a text-entry macro as something like X X map! usr /usr/group X Xand this is just as disastrous. The sequence that you use to define a Xremapped text-entry macro MUST NOT also appear in the macro text, because Xwith the "map!" mechanism, the sequence in the macro text will be infinitely Xexpanded. This sort of thing is especially pernicious for "map!"; it's very Xrare with "abbr", but can happen there, too, provided that the sequence Xconstituting the abbreviation name appears in the macro text and is bracketed Xby non-alphanumeric characters. X XA macro is just like a computer program, and can be induced to execute Xendless loops that either do nothing or else wreak great damage on your text. X X However, there is nevertheless a place for a recursive macro call, Xalmost exclusively in the realm of command-mode macros. Most often, such Xuseful recursive macros require just the sort of tail-recursion that 'vi' Xresentfully guards against. 'Vi' won't let you end a macro definition with Xthe same character that you use to name the macro, so that X X map ; 123456; X Xwon't work; but if you say, instead, X X map = ; X map ; 123456= X Xyou're set. If you invoke the remapped macro key ";" from command mode, then X'vi' will substitute "123456", and then find that "=" has been remapped to X";", that ";" has been remapped to "123456=", and repeat itself. This is Xobviously a useless macro, of course; but unlike the text-entry mode macros, Xfor reasons explained below, it isn't disastrous; it's merely useless. Useful Xtail-recursive macros exist. X XSidenote: in this regard, note the "remap" option, which is controlled by the Xex-escape commands "set remap" and "set noremap". When the remap option is Xset (and this is usually the default case) then remapped characters are tried Xrepeatedly until they are unchanged. If "o" is mapped to "O" and if "O" is Xmapped to "I", then if the remap option is set, "o" will be mapped to "I". XIf the remap option is not set, or to put it another way, if the "noremap" Xoption is set, then "o" will only be remapped to "O". Controlling the state Xof the remap switch can be used in very sophisticated recursive or chained Xmacros to control macro termination and macro flow, because since macros can Xbounce among modes, it's quite possible, and sometimes useful, to include as Xpart of the text of a macro, X X "...:set noremap^M...:set remap^M" X XThis allows the macro to toggle the remap switch transparently to the way the Xoption is set outside the context of the macro. X X XTerminating a recursive macro X------------------------------ X XRecursive macros aren't necessary very often -- the ex-escape "g" command Xserves pretty well -- but when they are necessary, you might wonder just how Xthey ever stop. The first answer is that ordinarily a macro can be Xterminated by hitting a keyboard interrupt -- the BREAK or DELETE key, or Xsometimes control-C. The second answer is that a 'vi' macro will terminate Xif somewhere in the middle of the macro it finds a command that it can't Xexecute successfully. X XLet's say that you want to count the times a given word or phrase occurs in Xyour text between your cursor position and the end of text. The macro X X map ; /Monty zuma/^M:!echo>>somefile^M^M= X Xif followed by X X map = ; X set nowrapscan X Xwhen invoked with the single keystroke ";" will recursively echo a blank line Xinto "somefile" each time the string "Monty zuma" is encountered in the Xcurrent text file. When the scan reaches the end of the file, the "/" Xcommand will fail because "nowrapscan" is set, and the macro will terminate. XSuch a macro differs from merely counting the lines on which the string X"Monty zuma" occurs, because "Monty zuma" might occur more than once on a Xsingle line. When it's done, and assuming that "somefile" was nonexistent or Xempty when the macro began, then the number of instances of "Monty zuma" from Xyour cursor position when you invoked the macro to the end of your text file Xwill correspond to the result of ":!wc -l somefile". X XIn the above example, note the double "^M" after the shell-escape "echo" Xcommand. Remember that a 'vi' macro merely duplicates a set of keystrokes. XHad you typed the command manually at the keyboard, the shell-escape "echo" Xcommand would have ended with a "Press return to continue" banner from 'vi'. XYou'd have had to hit RETURN after typing the "echo" command, then hit RETURN Xagain after the "echo" command completed, before doing anything else with X'vi', and the same principle applies to your macro. X XOn the other hand, remember also that some kinds of ex-escape commands do NOT Xrequire an extra RETURN after the command completes. Most common are the Xfiltering commands, like the ":.!date^M" command in the "dater" abbreviation Xdiscussed earlier. You must always keep in mind that the macro is merely Xdoing keystrokes for you, and that deciding upon the correct sequence of Xkeystrokes is up to you. The best approach to writing complex 'vi' macros is Xto take a pad and pencil and step through your projected macro keystroke by Xkeystroke to make sure your logic is correct. Only after you're sure that it Xis, should you actually enter the macro itself. X XThere are some peculiarities and limitations on just how well a macro can Xmimic your own keystrokes, and that will be discussed next. X XPeculiar limitations and restrictions on 'vi' macros X===================================================== X XSize X----- X XThis is probably the most exasperating restriction. In most implementations Xof 'vi' for microcomputers, the total text of "abbr", "map!", or "map" cannot Xexceed some small fixed limit, often the size of a single disk block. XMoreover, the text of any given abbreviated or remapped sequence, and the Xcontent of any named text buffer used as a macro, cannot be longer than some Xeven smaller fixed limit, often something like 128 characters. X XGetting around these restrictions takes some ingenuity. X XFor getting around the size of an individual macro definition, chain macros Xtogether. X XFor getting around the overall limit on macro text, there's really no answer Xexcept to use different sets of macros depending on the job you're doing at Xthe moment. An excellent suggestion is to keep a file of every complex macro Xyou've ever composed, where "complex" here means "hard to remember." Place Xthese macros in a text file, and put comments above each one to tell you what Xit does and why it works. Something like X X # This macro opens a window in a Compuserve Forum capture X # file to allow entry of a reply for automatic upload X # later on. It assumes that control-A has previously been X # mapped to the sequence ":set wrapmargin=3^M". Text marker X # "a" is set to the top of the block, and marker "b" is X # set to the bottom of the block, so later, the block from X # "a" to "b" can be sent to an upload file. X ?^#: [0-9][0-9]* ?^M/[0-9]/^MdwP/^$/^MP0irep ^[ma^Ao/post^[mbkO X X # This macro takes the text between text markers "a" and "b", X # inclusive, and appends it to the text file "tangen" for later X # upload to the Compuserve Tangent Forum. X :'a,'b!cat >>tangen X XIn these examples, the control characters are all printable ascii, so that XRETURN is represented by a caret and then an "M". In your own "macro Xnotebook" file, you should have the control characters made explicit, so Xthat, say, "^M" is a REAL carriage return rather than a caret and an M. You Xinsert such characters with the control-V prefix, as discussed earlier. X XYou'll never use such a file directly. Instead, you'll use the lines in it Xto select macros either to place into named text buffers -- you might want to Xput the first of the above macros into named buffer "x", and the second into Xnamed buffer "t", so that if you were reading your capture file and wanted to Xcompose a reply, you'd hit "@x", compose your reply, and then hit "@t" to Xsave it. X XAnother approach is to use the lines in the file to generate "map", or "abbr" Xor "map!" commands to place into the EXINIT environment variable later on. XYou do this by bringing up your macro notebook file in 'vi', prefixing each Xline you want to, say, map, with "map soandso ", yanking that line into a Xnamed buffer with a capital-letter name; then saying ":e! new_vi" and using Xthe "put" command to stick your accumulated text into the "new_vi" file. XMake sure that you don't re-save the notebook file; one of the nice things Xabout 'vi' is that it's a non-destructive editor. Now, in "new_vi", massage Xthe text so that it's all a massive EXINIT initialization string. At the Xbottom of the file, add "export EXINIT", and then "vi $*". Write the file, Xexit, and enter "sh new_vi", and you'll have a custom-remapped 'vi' for the Xpresent purpose. Often you'll find that only a very few such different Xcustomized 'vi's are needed. Only rarely, using this method, does it become Xirritating to be limited to, say, a 512-byte block of total macro text. X XA third approach is to customize a file called '.exrc' in the current Xdirectory: if different kinds of editing jobs are kept in different Xdirectories, each '.exrc' file will contain macros suitable for the job Xyou're working on. The format of a '.exrc' file: X X ab gatt General Agreement on Tariffs and Trade X map = :!date^M X map! qq rot E + dB/dt = 0 X set ai X XPutting and yanking to/from named buffers X------------------------------------------ X XSometimes you'll have inside a macro a directive to yank text into a named Xtext buffer, and 'vi' will tell you that you can't yank from inside a macro. XOr put inside a macro. This is usually a lie: when this error message comes Xup, it's probably because of a fault in 'vi's program logic. You will Xprobably be able to get around it by making the yank the first thing that Xhappens in the macro; sometimes, with particularly boneheaded implementations Xof 'vi', this will require that you split a single logical macro into two Xseparate keystrokes. X XWhich keys to remap? X--------------------- X XAs previously mentioned, some keys you can't remap at all in command mode. XNot much you can do about this. There are only a limited set of keys that Xdon't correspond to existing 'vi' commands, so unless you stick with that Xlimited set, you're eventually going to redefine an existing command key. XWhen you do, make sure it's not a command key that you have any pressing Ximmediate need for. You can avoid the command-key-name crunch to some extent Xby using the named buffers for your macros; very few applications really Xrequire 26 active non-volatile text buffers, so that leaves quite a few of Xthe named buffers from a-z available for use as macros. X X XConclusion X=========== X XWriting and using 'vi' macros is roughly equivalent to writing shell scripts, Xor other computer programs. The functionality is limited only by your skill Xin using 'vi's own capabilities, the tools available from Unix, and your Ximagination. If you can program in C, don't be afraid of writing a short C Xfilter that you can tie into a 'vi' macro to do things with text that you Xcan't see any other easy way to do. If you can't program in C, there are a Xmultitude of tools available anyway. How to use 'vi' in general, and how to Xuse the multifarious Unix tools, is beyond my scope here. Have fun. + END-OF-FILE macros chmod 'u=r,g=r,o=r' 'macros' set `wc -c 'macros'` count=$1 case $count in 34241) :;; *) echo 'Bad character count in ''macros' >&2 echo 'Count should be 34241' >&2 esac exit 0