home *** CD-ROM | disk | FTP | other *** search
- Vi Macros, Abbreviations, and Buffers
- Copyright (C) 1988 by Fred Buck; all rights reserved.
- Thanks to: Jean-Pierre Radley <jpradley!root@uunet.uu.net>
- Contributions:
- 1989 Maarten Litmaath <maart@cs.vu.nl>
- 1989 Eamonn McManus <emcmanus@cs.tcd.ie>
- Patchlevel: 1
-
- Heading summary:
- =================
-
- Introduction
-
- Non-macro stuff you really should know about when writing macros
-
- Text markers
- Text buffers
- The escape filter
-
- Simple description of the various 'vi' macro mechanisms
-
- Text abbreviation
- Keystroke remapping: text mode ("map!")
- Keystroke remapping: command mode ("map")
- Text-buffer execution
-
- More detail about 'vi' macro operation
-
- Mode-bouncing
- Chained macros
- Recursive macros
- Terminating a recursive macro
-
- Peculiar limitations and restrictions on 'vi' macros
-
- Size
- Putting and yanking to/from named buffers
- Which keys to remap?
-
- Conclusion
-
- Introduction
- =============
-
- 'Vi' offers a variety of "macro" facilities; a "macro" in this context is a
- mechanism whereby a small number of keystrokes, usually a single keystroke,
- is made to represent another set of keystrokes, so that typing the first set
- of keystrokes is equivalent to typing the second set. Applications for
- macros fall into three major classes:
-
- (a) during text entry, allowing a long, often-repeated
- string of text to be represented by a short abbreviation; for
- instance, in your biography of Ramaswamy Gopalikrishnan, you
- might assign "rgn" as a macro for the name of your subject;
-
- (b) existing 'vi' commands can be made invokable by
- keys other than the traditional ones, so that for instance if
- your terminal has function keys, you can remap the sequences
- generated by the function keys to existing 'vi' commands, and
- thereafter use the function keys instead of the standard 'vi'
- command keys;
-
- (c) complex operations that would take many keystrokes,
- or that would take a hard-to-remember sequence of keystrokes,
- can be tied to an easy-to-remember sequence of keystrokes, and
- be executed quickly and easily.
-
- If your primary interest lies in category (c), then 'vi' macros should be the
- LAST topic you take up when learning about 'vi', because you can't write
- macros to accomplish things you couldn't do yourself if you were typing
- everything in at the keyboard: your macros are only as good as your overall
- skill at using 'vi'. If you're not familiar with using the named and unnamed
- 'vi' text buffers, or with the use of text markers, or with the shell-escape
- ("!") filtration mechanism, the next section contains a bare-bones
- introduction to these things, but you should really play with them at some
- length yourself in order to see their full possibilities.
-
-
- Non-macro stuff you really should know about when writing macros
- =================================================================
-
- The following is only a very cursory summary of some basic 'vi' mechanisms
- that are essential to be familiar with when writing complex 'vi' macros.
- These summaries are intended to make at least intelligible the discussion
- that is to follow them. If you don't know about these mechanisms, and if
- you want to write complex 'vi' macros, you'd be well-advised to seek out
- a text on 'vi' read up on these things.
-
- Text Markers:
- --------------
-
- 'Vi' has 26 available text markers, corresponding to the 26 lowercase letters
- of the alphabet. A marker can be emplaced by moving the cursor to the place
- you want to mark, and typing "m" followed by the marker name you want to use,
- from "a" to "z". Typing "ma" marks that location with marker "a". To return
- to this location from somewhere else in the text, type "`a", where "`" is a
- backquote sign, or grave accent. The backquote moves the cursor to the
- precise location you were at when you emplaced the mark. If instead you're
- satisfied merely to go to the beginning of the line you were on when you
- emplaced the mark, type "'a", where "'" is a standard single-quote sign.
-
- A text region can often be defined by a cursor-movement command, and
- the single-quote and back-quote commands are cursor-movement commands.
- Placing text markers makes it much more easy to yank text into a buffer.
-
- Text buffers:
- --------------
-
- 'Vi' has a remarkable total of 36 user-reachable text buffers. 26 of these
- are static buffers, meaning that they don't change until the user tells them
- to change, and these are called the "named" text buffers, with names
- corresponding to the 26 letters of the alphabet. Don't confuse these "named"
- buffers with the 26 text-marker names; the two are entirely separate and
- independent. The text markers must always be in lowercase; the text-buffer
- names can be either uppercase or lowercase, although whether a text-buffer
- name is in uppercase or in lowercase changes the way text gets loaded into
- the buffer.
-
- To put text into a named buffer, type a double-quotation mark, then
- the name of the buffer, and then a cursor-movement command, like this:
-
- "ay`b
-
- which yanks into named text buffer "a" the text between your current cursor
- position and the exact location of text marker "b", which we assume you've
- previously set. The "yank" will end JUST BEFORE the exact location of text
- marker "b", if that is after the cursor; if it is before the cursor the
- "yank" happens as if cursor and marker were interchanged. To yank simply an
- entire line, use "yy" (or "Y") instead of "y" and the cursor-movement
- command; to yank N number of lines from your current cursor position, use
-
- "aNyy
-
- The double-quote mark, '"', is the 'vi' command character that indicates that
- you want to manipulate a text buffer.
-
- If you use an uppercase rather than a lowercase letter for the name
- of the text buffer, then whatever you yank into the buffer will be appended
- to the buffer, rather than over-writing the buffer's previous contents.
-
- To re-insert text from a text buffer, position the cursor where you
- want to make the insertion, and type a double-quote sign, then the name of
- the buffer (case doesn't matter for re-insertion), and then either "p" or
- "P":
-
- "ap
-
- If you use a lowercase "p", then the text is re-inserted after your current
- cursor position; if you use an uppercase "P", then the text is re-inserted
- before your current cursor position.
-
- In addition to the 26 named text buffers, 'vi' stacks deleted LINES
- in a set of 9 volatile buffers. By "volatile" I mean that the contents of
- these buffers change without the user's explicit direction. Another volatile
- buffer is called the "unnamed" buffer, in which the most-recently-deleted text
- resides, and from which the bare "p" and "P" commands work. This is the only
- buffer in which every piece of deleted text is put, the other nine buffers
- deal with complete lines only. They are reachable with the names "1" through
- "9", and hence are known as the numbered buffers. So if you delete some text
- in 'vi', the text you've just deleted is in the "unnamed" delete buffer and
- can be recovered with "p" or "P". If that text contains at least one line,
- it is placed into buffer "1" as well, whose previous contents are shifted into
- buffer "2" and so forth. Keep deleting text, and the text you deleted the
- first time will advance from buffer "1" to buffer "9". If you delete still
- more text, whatever is in buffer "9" is thrown away, and can no longer be
- recovered via the buffer mechanism. However, it can be recovered if you
- haven't written the file since you deleted the text:
-
- :w temp
- :e!
-
- The first command writes the current (modified) contents of the file you're
- editing to a temporary file called 'temp', the second command tells 'vi' to
- reload the original file WITHOUT saving the modifications you've made.
- They are, however, still available in 'temp'.
-
- The syntax for working with the numbered buffers is the same as that
- for the named buffers: to extract text from, say, delete buffer "1", you'd
- say
-
- "1p
-
- The "unnamed" delete buffer, in which the most-recently-deleted text is
- stored, is recoverable simply by typing a "p" or a "P".
-
- The escape filter
- ------------------
-
- An arbitrary set of text lines can be sent through a Unix or Xenix command
- and the result substituted for these lines in place. This is done within
- 'vi' by the command
-
- !<cursor-movement-command><Unix/Xenix command>
-
- which has the effect of sending the text lines beginning with the current
- line and ending with the line your cursor-movement-command has taken you to.
- If in 'vi' the cursor were placed at the beginning of this paragraph, "An
- arbitrary", and then the command
-
- !/has the effect/<RETURN>tr '[a-z]' '[A-Z]<RETURN>
-
- were entered, every alphabetic character from the cursor position to the end
- of the line containing the string "has the effect" would be forced to
- uppercase. Note that most versions of 'vi' operate only in whole-line
- increments when using the shell-escape filter; some versions of 'vi' allow
- the shell-escape filter to work on text areas that don't correspond exactly
- to line boundaries.
-
- Simple description of the various 'vi' macro mechanisms
- ========================================================
-
- (1) text abbreviation, which operates only in text-entry mode (the
- "abbr" ex-escape command); once set, an abbreviation works
- only in "vi" text-entry mode;
-
- (2) keystroke remapping, which can operate either in text-entry
- or in command mode (the "map!" and "map" ex-escape commands);
- once set, a "map!"'ed sequence is triggered only in text-entry
- mode, and a "map"'ed sequence is triggered only in "vi"
- command mode;
-
- (3) text-buffer execution, which operates only in command mode:
- once text has been placed in any of the named text buffers,
- that text can be executed as if it were a sequence of 'vi'
- commands.
-
- Text abbreviation
- ------------------
-
- Using macros during text-entry is almost always motivated by the goal of just
- saving keystrokes. The text-abbreviation macro in 'vi' is set up by going to
- "ex-escape" mode by typing a colon, and has the form
-
- abbr <abbreviation> <expansion>
-
- where an example might be
-
- abbr gatt General Agreement on Tariffs and Trade
-
- [Note that the name of the text abbreviation cannot contain an embedded
- space. This is a limitation of all the 'vi' macro mechanisms except for text-
- buffer execution, which uses the names of the named text buffers to define
- which macro to execute; a buffer name is a single alphabetic letter.]
-
- Once this is done, then while in text-entry mode, whenever you type
- a non-alphanumeric character followed by the string "gatt", 'vi' will examine
- the next character you type to see if it's non-alphanumeric, and if so, then
- "gatt" will be erased and "General Agreement on Tariffs and Trade" will be
- substituted for it. (Typing "gatt" at the very top of your text qualifies as
- a non-alphanumeric character followed by "gatt".)
-
- If you don't want a particular instance of "gatt" to get converted,
- even though it's preceded and followed by a non-alphanumeric character, you
- have to escape the first character following "gatt" by typing
-
- ^V
-
- (that is control-V), so if you want "gatt!" to appear, you type
-
- gatt^V!
-
- With text abbreviations, your text appears as you type it, and no
- transformation is performed on your specified abbreviation until you follow
- it with a non-alphabetic character (which includes an immediate exit from
- text-entry mode). This differentiates text abbreviation from text-mode
- keystroke-remapping using the "map!" command, which will be discussed soon.
-
- Text abbreviations can be canceled with the ex-escape "unabbr"
- command,
-
- unabbr gatt
-
- Most simple abbreviations can be unabbreviated via a simple ex-escape
- command, the kind you introduce by typing a colon from 'vi' command mode.
- But many cannot, especially mode-bouncing abbreviations, to be discussed
- later. For these, you must enter genuine "ex" mode by typing a capital Q
- ("Q") from 'vi' command mode. Then do your "unabbr gatt". To return to "vi"
- mode, enter "vi" or "visual" at the "ex" colon prompt.
-
- You can get a list of your currently active abbreviations by entering simply
- "abbr" in ex-escape mode.
-
- Keystroke remapping: text mode ("map!")
- ----------------------------------------
-
- As previously mentioned, the text abbreviation mechanism won't make a
- substitution on text that you type until you type a character AFTER the
- abbreviation that persuades 'vi' that you want 'vi' to expand the
- abbreviation. Also, 'vi' echoes each character of the abbreviation as you
- type it, just in case you really want the "abbreviation" to be a literal
- sequence of characters in your text. So with the previous example, if you're
- typing away and enter
-
- "I also want to introduce a new word, 'gattblather'..."
-
- then if you have "gatt" abbreviated for "General Agreement on Tariffs and
- Trade", your text line will remain as-is, with no substitution for the string
- "gatt" in "gattblather". Furthermore, an abbreviated sequence must be preceded
- as well as followed by a non-alphanumeric character for the substitution to be
- triggered.
-
- Keystroke-remapping works a bit differently. Keystroke-remapping is
- handled by the ex-escape "map" command, which takes two forms: "map" operates
- on characters that are typed in command mode, and "map!" operates on
- characters that are typed in text-entry mode. (Some versions of 'vi' may
- reverse this.)
-
- Staying with the previous example, the ex-escape command
-
- map! gatt General Agreement on Tariffs and Trade
-
- causes the key-sequence "gatt" to be instantly replaced by "General Agreement
- on Tariffs and Trade" wherever it's typed. The abbreviation mechanism waits
- to see what the NEXT character after the abbreviation is going to be, before
- deciding whether to make a substitution, and if the sequence wasn't preceded
- by a non-alphanumeric character (or by the top of the file), the abbreviation
- mechanism doesn't start working at all.
-
- By contrast, the keystroke-remapping mechanism instead starts a per-character
- timer that begins whenever you type the first character of a remapped
- sequence. When you type "g", nothing will be echoed to your screen unless
- you type some character other than "a", or unless you type nothing at all
- within a timeout period, typically about two seconds long. In short, 'vi'
- watches to see if you've typed the beginning of a remapped sequence, and if
- the sequence is longer than a single character, 'vi' waits to see if you'll
- type the complete sequence, in which case 'vi' will supply whatever remapping
- you've specified for the sequence you've just typed. This will happen
- regardless of what follows the remapped sequence. So if you've remapped
- "gatt" to "General Agreement on Tariffs and Trade", then entering
-
- "I also want to introduce a new word, 'gattblather'..."
-
- in text mode will yield
-
- "I also want to introduce a new word, 'General Agreement
- on Tariffs and Tradeblather'..."
-
- As with abbreviations "^V" will let you escape the mapping, but this time
- the macro has to be preceded rather than followed by it. The following will
- get you a plain "gatt", regardless of how fast you type it:
-
- ^Vgatt
-
- To un-map! a previously map!'ed text-mode sequence like "gatt", use the ex-
- escape command
-
- unmap! gatt
-
- Most simple map!'ings can be un-map!'ed via a simple ex-escape command, the
- kind you introduce by typing a colon from 'vi' command mode. But many
- cannot, especially mode-bouncing map!'ings, to be discussed later. For
- these, you must enter genuine "ex" mode by typing a capital Q ("Q") from 'vi'
- command mode. Then do your "unmap! gatt". To return to "vi" mode, enter
- "vi" or "visual" at the "ex" colon prompt.
-
- You can get a list of your currently active text-mode remapped sequences by
- entering merely "map!" in ex-escape mode.
-
- NEVER MAKE A map!'ed DEFINITION RECURSIVE! More on this later.
-
-
- Keystroke remapping: command mode ("map")
- ------------------------------------------
-
- Sequences that you've made into text abbreviations, or that you've remapped
- using "map!", are triggered only when you're in text-entry mode, and are not
- triggered in command mode. (Note, however, that an ex-escape command line is
- considered by 'vi' to constitute text-entry mode.) Command-mode remapping is
- done with the "map" ex-escape command -- note the missing exclamation point
- -- and works identically with text-entry remapping except that "map"'ed
- sequences are triggered only in command mode and are not triggered in text-
- entry mode.
-
- Command-mode keystroke remappings can be canceled the same way text-
- entry mode keystroke remappings can, except without the exclamation point:
-
- unmap ;
-
- Some 'vi' command keys simply cannot be remapped in command mode; the set of
- these keys varies according to the implementation of 'vi'. The most common
- unremappable keys are ":" and "u".
-
- You can get a list of your currently active command-mode remapped sequences
- by entering merely "map" in ex-escape mode.
-
-
- Text-buffer execution
- ----------------------
-
- This topic, which is often glossed over or omitted entirely in most
- proprietary 'vi' documentation, can be stated very simply: any named text
- buffer can be treated as a 'vi' command-mode macro by typing the at-sign
- character ("@") followed by the name of the buffer. If named buffer "a"
- contains
-
- H!Lsort
-
- then "@a" from 'vi' command mode will sort the lines now on the terminal
- screen. The same would of course be true of a "map" that said the same
- thing; this example is for illustration.
-
- The ability to execute a named text buffer as if the buffer were a
- sequence of commands allows 'vi' macros sometimes to operate in a self-
- modifying way, because a macro can load a text buffer from text in the
- current file and then invoke that text buffer as a command macro, without
- having to know in advance what the text is that will be loaded into the
- buffer.
-
- More detail about 'vi' macro operation
- =======================================
-
- Mode-bouncing
- --------------
-
- The various 'vi' macro facilities differ in whether they're triggered in text-
- entry or in command mode. Text-abbreviation is triggered only in text-entry
- mode; the "map!" command creates macros that are triggered only in text-entry
- mode; the "map" command creates macros that are triggered only in command
- mode; and the "@" command works only in command mode.
-
- But all this just means that the >trigger< for the macro must come
- within the mode that the macro corresponds to. Any macro, regardless of
- type, can switch between the invocation mode and other modes and back again.
- This is what's meant by a "mode-bouncing" macro. Any 'vi' macro can go
- from text-entry to command mode, to ex-escape mode, or to vi-mode. You just
- define the macro with the exact keystroke sequence that you yourself would
- use if you were changing modes. I refer to macros that shift from one mode
- to another as "mode-bouncing" macros.
-
- Control characters are entered into macro definitions by preceding
- them with a control-V. The result, after the control character is typed,
- appears as something like "^M" for a carriage return, or "^[" for an ESCAPE.
- Say for the sake of argument you've abbreviated "dater" this way:
-
- abbr dater ^M^[:.!date +\%D^MkJA
-
- where "^M" stands for the keystroke sequence "control-V" followed by "control-
- M", and "^[" stands for "control-V" "<ESCAPE>". Try jotting this down on a
- piece of paper, or printing it, and then trying the definition out by typing
- each keystroke manually. Then try defining the abbreviation with the ex-
- escape "abbr" command, going into text entry mode, and typing text that
- contains the string "dater" preceded and followed by a non-alphanumeric
- character, say,
-
- I wonder if today is dater?
-
- Note that strange things might happen if instead of an abbreviation, you
- used "map!" to make this macro, and then made a minor spelling error in
- the middle of some other sentence and said "validater". This difference
- is the reason that 'vi' retains both "abbr" and "map!". Even with
- "abbr", it's possible to collide with some valid use of "dater", as in
- "Please take this form and run it through the dater;" the lesson here is,
- the main problem with 'vi' text macros is pilot error, the failure to
- anticipate future collisions that will have unintended effects.
-
- [Sidenote: the reason that '%' is preceded by a backslash in the above
- example is that on the "ex" command line, '%' is a magic character standing
- for the name of the file currently being edited, and therefore must be
- escaped to avoid this special meaning. The two most common other
- characters that have special significance on the "ex" command line are
- "#", which stands for the last file previously edited, and "!", which
- stands either for the shell-escape, shell-filter, or command-history
- mechanisms depending on its position within the line. All of these
- characters should be escaped on the "ex" command line when you don't
- want "ex" to see them specially.]
-
- Text-entry macros that bounce between modes are notoriously difficult to
- cancel ("unabbr" or "unmap!") with a simple ex-escape command, the kind you
- introduce by typing a colon in 'vi' command mode. This is because 'vi'
- believes that a simple ex-escape command line is being typed in text-entry
- mode, therefore it expands the name of the abbreviation or remap!'ing as you
- enter it. The only effective way to get rid of such macros (and probably you
- should make it your habit to use this method to unmap ALL macros, just so you
- don't have to risk making a mistake evaluating which macro should be erased
- this way and which should not) is to type a capital Q ("Q") in 'vi' command
- mode, which puts you into genuine "ex" mode. From now on, 'vi' can't see
- you. Do your "unabbr" or "unmap!", then enter "vi" or "visual" at the next
- colon prompt to return to 'vi'.
-
- Chained macros
- ---------------
-
- Macros, being merely pre-defined sequences of keystrokes, can invoke other
- macros, anywhere in their definition. Such "sub-macros" operate like
- subroutines in a computer program. Take the following example:
-
- map = :set wm=3^M
- map ; ^[=oThe End^[O
-
- What this does is to first enter 'vi' command mode, in case we're not already
- there; invoke the "=" macro to set word-wrap with a hotzone of 3 characters,
- open text for entry, insert the words "The End" on a line by themselves, exit
- text-entry mode, open a new line above "The End", and let the user start
- typing in whatever text is to be ended by "The End".
-
- This can be done at any number of levels; the "=" macro could itself call
- other macros, and so forth.
-
-
- Recursive macros
- -----------------
-
- A macro that invokes itself is called a "recursive" macro. 'Vi' tries to
- limit the creation of recursive macros by prohibiting macros that embody
- "tail recursion", which is to say, a macro that ends by invoking itself.
- This policing system is only partial. Nothing stops you from imbedding a
- recursive macro call INSIDE (as opposed to at the end of) a macro definition.
- Although 'vi' will object to a definition like
-
- map! $ 123456$
-
- it'll accept
-
- map! $ 123$456
-
- THIS IS AN ABSOLUTE DISASTER. You probably wouldn't make this mistake
- yourself, but you MIGHT define a text-entry macro as something like
-
- map! usr /usr/group
-
- and this is just as disastrous. The sequence that you use to define a
- remapped text-entry macro MUST NOT also appear in the macro text, because
- with the "map!" mechanism, the sequence in the macro text will be infinitely
- expanded. This sort of thing is especially pernicious for "map!"; it's very
- rare with "abbr", but can happen there, too, provided that the sequence
- constituting the abbreviation name appears in the macro text and is bracketed
- by non-alphanumeric characters.
-
- A macro is just like a computer program, and can be induced to execute
- endless loops that either do nothing or else wreak great damage on your text.
-
- However, there is nevertheless a place for a recursive macro call,
- almost exclusively in the realm of command-mode macros. Most often, such
- useful recursive macros require just the sort of tail-recursion that 'vi'
- resentfully guards against. 'Vi' won't let you end a macro definition with
- the same character that you use to name the macro, so that
-
- map ; 123456;
-
- won't work; but if you say, instead,
-
- map = ;
- map ; 123456=
-
- you're set. If you invoke the remapped macro key ";" from command mode, then
- 'vi' will substitute "123456", and then find that "=" has been remapped to
- ";", that ";" has been remapped to "123456=", and repeat itself. This is
- obviously a useless macro, of course; but unlike the text-entry mode macros,
- for reasons explained below, it isn't disastrous; it's merely useless. Useful
- tail-recursive macros exist.
-
- Sidenote: in this regard, note the "remap" option, which is controlled by the
- ex-escape commands "set remap" and "set noremap". When the remap option is
- set (and this is usually the default case) then remapped characters are tried
- repeatedly until they are unchanged. If "o" is mapped to "O" and if "O" is
- mapped to "I", then if the remap option is set, "o" will be mapped to "I".
- If the remap option is not set, or to put it another way, if the "noremap"
- option is set, then "o" will only be remapped to "O". Controlling the state
- of the remap switch can be used in very sophisticated recursive or chained
- macros to control macro termination and macro flow, because since macros can
- bounce among modes, it's quite possible, and sometimes useful, to include as
- part of the text of a macro,
-
- "...:set noremap^M...:set remap^M"
-
- This allows the macro to toggle the remap switch transparently to the way the
- option is set outside the context of the macro.
-
-
- Terminating a recursive macro
- ------------------------------
-
- Recursive macros aren't necessary very often -- the ex-escape "g" command
- serves pretty well -- but when they are necessary, you might wonder just how
- they ever stop. The first answer is that ordinarily a macro can be
- terminated by hitting a keyboard interrupt -- the BREAK or DELETE key, or
- sometimes control-C. The second answer is that a 'vi' macro will terminate
- if somewhere in the middle of the macro it finds a command that it can't
- execute successfully.
-
- Let's say that you want to count the times a given word or phrase occurs in
- your text between your cursor position and the end of text. The macro
-
- map ; /Monty zuma/^M:!echo>>somefile^M^M=
-
- if followed by
-
- map = ;
- set nowrapscan
-
- when invoked with the single keystroke ";" will recursively echo a blank line
- into "somefile" each time the string "Monty zuma" is encountered in the
- current text file. When the scan reaches the end of the file, the "/"
- command will fail because "nowrapscan" is set, and the macro will terminate.
- Such a macro differs from merely counting the lines on which the string
- "Monty zuma" occurs, because "Monty zuma" might occur more than once on a
- single line. When it's done, and assuming that "somefile" was nonexistent or
- empty when the macro began, then the number of instances of "Monty zuma" from
- your cursor position when you invoked the macro to the end of your text file
- will correspond to the result of ":!wc -l somefile".
-
- In the above example, note the double "^M" after the shell-escape "echo"
- command. Remember that a 'vi' macro merely duplicates a set of keystrokes.
- Had you typed the command manually at the keyboard, the shell-escape "echo"
- command would have ended with a "Press return to continue" banner from 'vi'.
- You'd have had to hit RETURN after typing the "echo" command, then hit RETURN
- again after the "echo" command completed, before doing anything else with
- 'vi', and the same principle applies to your macro.
-
- On the other hand, remember also that some kinds of ex-escape commands do NOT
- require an extra RETURN after the command completes. Most common are the
- filtering commands, like the ":.!date^M" command in the "dater" abbreviation
- discussed earlier. You must always keep in mind that the macro is merely
- doing keystrokes for you, and that deciding upon the correct sequence of
- keystrokes is up to you. The best approach to writing complex 'vi' macros is
- to take a pad and pencil and step through your projected macro keystroke by
- keystroke to make sure your logic is correct. Only after you're sure that it
- is, should you actually enter the macro itself.
-
- There are some peculiarities and limitations on just how well a macro can
- mimic your own keystrokes, and that will be discussed next.
-
- Peculiar limitations and restrictions on 'vi' macros
- =====================================================
-
- Size
- -----
-
- This is probably the most exasperating restriction. In most implementations
- of 'vi' for microcomputers, the total text of "abbr", "map!", or "map" cannot
- exceed some small fixed limit, often the size of a single disk block.
- Moreover, the text of any given abbreviated or remapped sequence, and the
- content of any named text buffer used as a macro, cannot be longer than some
- even smaller fixed limit, often something like 128 characters.
-
- Getting around these restrictions takes some ingenuity.
-
- For getting around the size of an individual macro definition, chain macros
- together.
-
- For getting around the overall limit on macro text, there's really no answer
- except to use different sets of macros depending on the job you're doing at
- the moment. An excellent suggestion is to keep a file of every complex macro
- you've ever composed, where "complex" here means "hard to remember." Place
- these macros in a text file, and put comments above each one to tell you what
- it does and why it works. Something like
-
- # This macro opens a window in a Compuserve Forum capture
- # file to allow entry of a reply for automatic upload
- # later on. It assumes that control-A has previously been
- # mapped to the sequence ":set wrapmargin=3^M". Text marker
- # "a" is set to the top of the block, and marker "b" is
- # set to the bottom of the block, so later, the block from
- # "a" to "b" can be sent to an upload file.
- ?^#: [0-9][0-9]* ?^M/[0-9]/^MdwP/^$/^MP0irep ^[ma^Ao/post^[mbkO
-
- # This macro takes the text between text markers "a" and "b",
- # inclusive, and appends it to the text file "tangen" for later
- # upload to the Compuserve Tangent Forum.
- :'a,'b!cat >>tangen
-
- In these examples, the control characters are all printable ascii, so that
- RETURN is represented by a caret and then an "M". In your own "macro
- notebook" file, you should have the control characters made explicit, so
- that, say, "^M" is a REAL carriage return rather than a caret and an M. You
- insert such characters with the control-V prefix, as discussed earlier.
-
- You'll never use such a file directly. Instead, you'll use the lines in it
- to select macros either to place into named text buffers -- you might want to
- put the first of the above macros into named buffer "x", and the second into
- named buffer "t", so that if you were reading your capture file and wanted to
- compose a reply, you'd hit "@x", compose your reply, and then hit "@t" to
- save it.
-
- Another approach is to use the lines in the file to generate "map", or "abbr"
- or "map!" commands to place into the EXINIT environment variable later on.
- You do this by bringing up your macro notebook file in 'vi', prefixing each
- line you want to, say, map, with "map soandso ", yanking that line into a
- named buffer with a capital-letter name; then saying ":e! new_vi" and using
- the "put" command to stick your accumulated text into the "new_vi" file.
- Make sure that you don't re-save the notebook file; one of the nice things
- about 'vi' is that it's a non-destructive editor. Now, in "new_vi", massage
- the text so that it's all a massive EXINIT initialization string. At the
- bottom of the file, add "export EXINIT", and then "vi $*". Write the file,
- exit, and enter "sh new_vi", and you'll have a custom-remapped 'vi' for the
- present purpose. Often you'll find that only a very few such different
- customized 'vi's are needed. Only rarely, using this method, does it become
- irritating to be limited to, say, a 512-byte block of total macro text.
-
- A third approach is to customize a file called '.exrc' in the current
- directory: if different kinds of editing jobs are kept in different
- directories, each '.exrc' file will contain macros suitable for the job
- you're working on. The format of a '.exrc' file:
-
- ab gatt General Agreement on Tariffs and Trade
- map = :!date^M
- map! qq rot E + dB/dt = 0
- set ai
-
- Putting and yanking to/from named buffers
- ------------------------------------------
-
- Sometimes you'll have inside a macro a directive to yank text into a named
- text buffer, and 'vi' will tell you that you can't yank from inside a macro.
- Or put inside a macro. This is usually a lie: when this error message comes
- up, it's probably because of a fault in 'vi's program logic. You will
- probably be able to get around it by making the yank the first thing that
- happens in the macro; sometimes, with particularly boneheaded implementations
- of 'vi', this will require that you split a single logical macro into two
- separate keystrokes.
-
- Which keys to remap?
- ---------------------
-
- As previously mentioned, some keys you can't remap at all in command mode.
- Not much you can do about this. There are only a limited set of keys that
- don't correspond to existing 'vi' commands, so unless you stick with that
- limited set, you're eventually going to redefine an existing command key.
- When you do, make sure it's not a command key that you have any pressing
- immediate need for. You can avoid the command-key-name crunch to some extent
- by using the named buffers for your macros; very few applications really
- require 26 active non-volatile text buffers, so that leaves quite a few of
- the named buffers from a-z available for use as macros.
-
-
- Conclusion
- ===========
-
- Writing and using 'vi' macros is roughly equivalent to writing shell scripts,
- or other computer programs. The functionality is limited only by your skill
- in using 'vi's own capabilities, the tools available from Unix, and your
- imagination. If you can program in C, don't be afraid of writing a short C
- filter that you can tie into a 'vi' macro to do things with text that you
- can't see any other easy way to do. If you can't program in C, there are a
- multitude of tools available anyway. How to use 'vi' in general, and how to
- use the multifarious Unix tools, is beyond my scope here. Have fun.
-