The mappings shown under headings (1)-(4) below all appear in my EXINIT variable (see end of this file), though while trying them out, you might want to give them as bottom-line commands. (Or, once you are using the first one, as lines executed using that mapping.) They work in the two versions of vi I have used until recently: the old BSD vi, and the vi on SUN unix. I have recently begun using vim on linux, and have supplemented my comments with notes on how these mappings and their use have to be modified in this environment.
In the descriptions below, I show non-printing characters as they
appear in vi; e.g. ^M, ^V, ^[, etc..
To actually use these mappings, you need to replace these
non-printing characters by the characters they represent.
(To enter ^M in a file or on the bottom line,
use the sequence of two
keystrokes ^V^M ; i.e., CTR-V CTR-M.
Similarly, to get ^[ use ^V^[ and
to get ^V itself, use ^V^V .)
The particular characters or strings that you map are unimportant, of
course; you can substitute others of your choice for
my ^O, ^E, etc..
In vim, it is also possible to enter
in one's command-lines escape-sequences consisting of
ordinary characters and representing the above,
e.g., <C-O> for ^O , <C-M> or
<CR> for ^M, and <C-]> or
<ESC> for ^].
(1) map ^O "ayy:@a^M
( map <C-O> "ayy:@a<CR> in vim)
When this has been executed, the keystroke ^O given in
screen mode causes whatever line the cursor is currently on to be
executed as a bottom-line command.
Thus, one can work out complicated search commands, substitutions, mappings, etc., edit them with vi, and try them out and fine-tune them until one has what one wants. Even for commands that don't require great ingenuity, it may be useful to have a bank of them in a file for repeated use. For instance, in TeX files I am writing, I keep a bank of around 60 pattern-search commands to look for various errors in formatting I have found myself making over the years.
I also use it for sending e-mail:
I keep mail related to various topics in separate files, and to send
such e-mail, I will generally draft it at the end of the file,
preceded by a line
+,$w !mail whoever@wherever,
which I execute when it is ready.
In LaTeXing my preprints, I like to keep the source files in one
directory,
Papers/p,
and auxiliary matter (dvi files and log files,
journals' style class files, etc.) in a "utilities"
subdirectory,
Papers/p/u.
I edit the source files with
Papers/p as
my current directory, and
when I have a version ready to LaTeX, I execute
with ^O a line
w|!cd u;latex ../%
which I keep in the file.
This spawns a shell which moves to the "utilities" directory,
LaTeXs the file from there (accessing the style class files there
if needed), and puts in that directory the various
associated files; including the dvi file, to which I keep an
instance of xdvi pointed.
How does the ^O mapping work?
The first part, "ayy , yanks the content of the
current line to buffer "a".
The second part is the bottom-line command :@a
(terminated by a carriage return, ^M ), which
executes the contents of buffer "a".
If you precede the keystroke ^O by a positive
integer n, this will similarly yank and execute
the next n lines.
(However, with SUN vi, there is a glitch:
If the n lines in question include a line with a global command,
address g/pattern/command,
then the lines after that one are ignored.
This does not happen when a script file is executed in ex
mode; it seems to be a bug associated with visual mode.)
Many of the commands to which I apply the ^O trick
are themselves commands to map keystroke combinations;
and this introduces another
complication: the distinction between the interpretation
of characters in the command to do the mapping, and in
the mapping itself.
In particular, if the desired action involves a pipeline
character |, this must appear in the
command as ^V|, otherwise when one executes
the command, vi will interpret the command to mean that
only what appears before the pipeline is part of the mapping command,
and that what follows is a second command.
There is a further complication in vim, where for
reasons I don't understand, two ^V's are needed before
a pipeline in a mapping command is executed using
^O (although one suffices
in one's VIMINIT variable, or in a script file that one sources).
Moreover, if the command contains a carriage-return ^M or an
escape character ^[, these must appear in lines to be
executed by ^O as ^V^M and as ^V^[.
Hence in reading the points that follow, remember:
If using vim, all
the occurrences of ^V, ^M or ^[
in the commands shown below must be replaced by
^V^V, ^V^M and ^V^[ respectively if
these commands are given in lines to be executed using ^O.
(For the cases of ^V^M and ^V^[, an
alternative is to replace former with the escape sequence
<C-M> or <CR> and the latter with
<C-[> or <ESC>.
But I know no way to get a mapping whose effect
contains a pipeline than to input it with ^V^V|.
Note again that the above rule does not apply if these
commands are executed in another way, such as a sourced command
script or one's VIMINIT variable.)
(2) map =m :'x+kw^V|'w,'ymo'z-^M
This allows one to move a block of lines from one point in the
file to another: one gives the line above the block the mark
x (i.e., one puts the cursor
on that line and types mx in screen mode),
gives the last line of the block the mark y , and
gives the line above which one wants to block to be moved the mark
z , then types =m (still in screen
mode), and the move is made.
After the move, the position from which the block was moved is still
marked x , so one can revisit it with the
movement command 'x (and perhaps select some nearby
material that one also wants to move).
The beginning and end of the moved block are marked w
and y , so that one can also find them easily.
(3) map ^^ :w^V|e#^M
This causes the keystroke ^^ to be equivalent to
the bottom-line command w|e# , i.e., write the
current file and return to the previous file.
(Note: the keystroke ^^ involves hitting three
keys simultaneously: CTRL, CAP and 6.)
(4) map! ^E ^[Ea
This causes the keystroke ^E , when given
in insert mode, to jump to the next end-of-word location,
and reenter insert mode there.
So if I want to change They say
to They are saying , I enter insert mode
at the end of They , type " are", hit
^E , and type ing.
To skip two words, hit ^E^E , etc.
(5) A trick for checking matched parentheses.
I don't like using the "showmatch" option in vi; it slows down
my typing and is distracting.
But when I have finished writing or editing some mathematical work,
I want to know that parentheses match.
To do this, I keep the string ([{ at the beginning
of the file, and }]) at the end.
I put the cursor on one of these characters, say the (
at the beginning, and type the keystroke % .
If left and right parentheses ( ... )
match within the file, the cursor
will move (after a second or so) to the ) at the end.
If there is an unmatched ) in the file, it will
instead find that.
If, on the other hand, there is an unmatched ( , it
will not go anywhere.
In that case, one can find the unmatched ( by
going to the other end of the file, putting the cursor on
the ) there, and again typing % .
Of course, the above ([{ and }]) have
to be kept from being treated as part of the file;
this can easily be done by putting them in comment lines.
(Mathematical writing also occasionally uses unmatched parentheses;
e.g., in naming half-open intervals (a, b].
Such cases, if they are few, can be kludged by matching these symbols
with commented-out parentheses and brackets.
But if they are common in a file, it is probably simplest to forego
using, on that file, the part of this search
that handles those symbols.)
In vim, at least in the version on the system I am on, the above
matching trick occasionally fails, in peculiar circumstances which I
don't understand, involving double-quotes between the parentheses.
E.g., in a file you are editing with vim, try typing in the
6-character string ("(")) (double-quotes and all),
put the cursor on the first or last symbol of that string and
hit %; and see what happens.
There are still stranger example involving the interaction of
double quotes and newlines.
(Fortunately, in the source files of my math papers,
double quotes almost never occur.)
(6) When you're not sure what changes you have made.
Sometimes I discover that I have suspended a vi session hours or
days earlier, and perhaps later edited the same file, so that the
existing file and the buffer of the suspended session may each
embody changes that the other does not.
The first thing I do on re-opening the suspended session is
hit ^G (or equivalently, give the bottom line
command :file ).
If the information that is then shown on the bottom
line does not contain the word [Modified], I know
that I wrote all changes before suspending the vi session, and
can safely quit this instance of vi.
If it does say [Modified], I will give the bottom-line
command :w !diff % - | less.
This gives a comparison between the contents of the buffer
(the "-" argument of "diff") and the current state of the file
(the "%" argument), allowing one to compare these, and decide
what parts, if any, of one version to copy into the other.
(The above trick is less necessary when using vim,
where the "undo" command u can be applied
recursively; but it is still helpful at times.)
(7) Getting where you want with a global.
It is easy to give a global command that will, say,
go to each line matching pattern1, find the next line
after it that matches pattern2,
and collect those lines for
you: g/pattern1//pattern2/ya X will
collect the desired lines in buffer x.
You can then put them into the file with the on-screen command
"xp or the bottom-line command :pu x
and examine them.
But what if you want to capture in this way the second
occurrence of pattern2 after each occurrence
of pattern1, or more generally, the first
occurrence of pattern3 after the first occurrence
of pattern2 after each occurrence
of pattern1?
The obvious sorts of commands won't work: e.g.,
g/pattern1//pattern2/|/pattern3/ya X
will write to the screen the occurrence
of pattern2 after each occurrence
of pattern1 (which you did not want, but is generally
not a problem), and then yank to buffer x
the occurrence of pattern3 following that same
occurrence of pattern1, not following the
intermediate occurrence of pattern2.
A little change that gives the desired behavior is to add a
semicolon after /pattern2/ in this command.
The semicolon causes vi to reset the location of "." (the "current
line") from the line found by the "global" command to the line just
found; so that the next search is relative to that line, rather than
relative to the line found by the global.
An additional change, which will get rid of the unintended behavior
of writing the
occurrences of pattern2 to the screen,
is to give it, instead something harmless to do that you won't notice --
e.g., marking each line found by the pattern2 search
as location "a".
Thus, we get the command
g/pattern1//pattern2/;ka|/pattern3/ya X ,
which does what we wanted to start with.
(The one unintended side-effect is that at the end of the command,
the last location found by the search for pattern2
is marked as location "a".
So you just have to be sure that you hadn't previously marked
as "a" some location you didn't want to lose.)
Needless to say, yanking isn't the only thing you may want to use this trick for. The thing to remember is that in a global command, pattern-searches are relative to the lines found in the original global search, unless a semicolon is used within the global command to re-set that location.
Entering mappings (1)-(4) in one's EXINIT.
Below is a simplified version of the line in my
.login file that
contains the mapping commands described in points (1)-(4) above.
(Simplified in that it leaves out other settings and mappings that
are either well-known, or of less general interest.
In the same file I have a line setting my VIMINIT,
similar but adapted to vim.)
setenv EXINIT 'map ^O "ayy:@a^M|map ^^ :w^V|e#^M|map! ^E ^[Ea|'"map =m :'x+kw^V|'w,'ymo'z-^M"
(Observe that the first part of the command, which contains the
mappings described under (1), (3) and (4) above,
is enclosed in single-quotes, '...',
while the mapping described under (2), placed at the
end, is enclosed in double-quotes, "...".
This is because mapping (1) itself contains a double-quote,
and hence can't be quoted using double-quotes, while (2)
involves several single-quotes,
hence needs double-quotes to quote it.)
If you copy this line into your own .login file,
remember to replace strings ^M etc. with the corresponding
genuine control-characters.
Once you have made this addition to your .login file,
these vi mappings will be in effect in all future login sessions.
You can get them to take effect in your current session by giving the
command source .login .