Vi Macros, Abbreviations, and Buffers
Heading summary:
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
-
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;
-
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;
-
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.