All of the features discussed so far are of interest
whether you are editing English text or program source code. However,
there are a number of additional features that are of interest chiefly
to programmers. These include indentation control, searching for the
beginning and end of procedures, and using ctags
.
The following discussion is adapted from documentation provided by Mortice Kern Systems with their excellent implementation of vi for DOS and Windows-based systems, available as a part of the MKS Toolkit or separately as MKS Vi. It is reprinted by permission of Mortice Kern Systems.
The source code for a program differs from ordinary text in a number of ways. One of the most important of these is the way in which source code uses indentation. Indentation shows the logical structure of the program: the way in which statements are grouped into blocks. vi provides automatic indentation control. To use it, issue the command:
:set autoindent
Now, when you indent a line with spaces or tabs, the following lines will automatically be indented by the same amount. When you press [RETURN] after typing the first indented line, the cursor goes to the next line and automatically indents the same distance as the previous line.
As a programmer, you will find this saves you quite a bit of work getting the indentation right, especially when you have several levels of indentation.
When you are entering code with autoindent enabled, typing [CTRL-T] at the start of a line gives you another level of indentation and typing [CTRL-D] takes one away.
We should point out that [CTRL-T] and [CTRL-D] are typed while you are in insert mode, unlike most other commands, which are typed in command mode.
There are two additional variants of the [CTRL-D] command.[8]
[8] These do not work in elvis 2.0.
^ ^D
When you type ^ ^D
([^] [CTRL-D]), vi
shifts the cursor back to the beginning of the line, but only for
the current line. The next line you enter will start at
the current auto-indent level.
This is particularly useful for entering C preprocessor commands
while typing in C/C++ source code.
0 ^D
When you type 0 ^D
, vi
shifts the cursor back to the beginning of the line. In addition,
the current auto-indent level is reset to zero; the next line you
enter will not be auto-indented.[9]
[9] The nvi 1.79 documentation has these two commands switched, but the program actually behaves as described here.
Try using the autoindent
option when you are entering
source code. It simplifies the job of getting indentation
correct. It can even sometimes help you avoid bugs (e.g., in C
source code, where you usually need one closing curly brace
(}
) for every
level of indentation you go backwards).
The <<
and >>
commands are also helpful when indenting
source code.
By default, >>
shifts a line right eight spaces
(i.e., adds eight spaces of indentation)
and <<
shifts a line left eight spaces.
For example, move the cursor to the beginning of a line and
press the >
key twice (>>
).
You will see the line move right.
If you now press the <
key twice (<<
), the line will move back again.
You can shift a number of lines by typing the number followed
by >>
or <<
.
For example, move the cursor to the first line of
a good-size paragraph and type 5>>
.
You will shift the first five lines in
the paragraph.
The default shift is eight spaces (right or left). This default can be changed with a command like:
:set shiftwidth=4
You will find it convenient to have a shiftwidth that is the same size as the width between tab stops.
vi attempts to be smart when doing indenting. Usually, when you see text indented by eight spaces at a time, vi will actually insert tab characters into the file, since tabs usually expand to eight spaces. This is the UNIX default; it is most noticable when you type a tab during normal input, and when files are sent to a printer -- UNIX expands them with a tab stop of eight spaces.
If you wish, you can change how vi represents
tabs on your screen, by changing the tabstop
option. For example, if you have something that is deeply indented,
you might wish to have use a tab stop setting of every four
characters, so that the lines will not wrap.
The following command will make this change:
:set tabstop=4
NOTE: Changing your tab stops is not recommended. Although vi will display the file using an arbitrary tabstop setting, the tab characters in your files will still be expanded using an eight-character tab stop by every other UNIX program. Eight-character tab stops are one of the facts of life on UNIX, and you should just get used to them.
Sometimes indentation won't work the way you expect, because what you believe to be a tab character is actually one or more spaces. Normally, your screen displays both a tab and a space as whitespace, making the two indistinguishable. You can, however, issue the command:
:set list
This alters your display so that a tab appears as the control
character ^I
and an end-of-line appears as a $
.
This way, you can spot a true space, and you can see extra spaces
at the end of a line. A temporary equivalent is the
:l
command.
For example, the command:
:5,20 l
displays lines 5 through 20, showing tab characters and end-of-line characters.
The characters (
,
[
, {
,
and <
can all be called
opening brackets. When the cursor is resting on one of these
characters, pressing the %
key moves the cursor from the opening
bracket forward to the corresponding closing
bracket -- )
,
]
,
}
,
or >
-- keeping in mind the usual
rules for nesting brackets.[10]
For example, if you were to move the cursor to the
first (
in:
[10] Of the versions tested, only nvi supported matching
<
and>
with%
. vile lets you set an option with the sets of pairs of characters that match for%
.
if ( cos(a[i]) > sin(b[i]+c[i]) ) { printf("cos and sin equal!\n"); }
and press %
, you would see that the cursor jumps to the
parenthesis at the end of the line. This is the closing
parenthesis that matches the opening one.
Similarly if the cursor is on one of the closing bracket
characters, pressing %
will move the cursor backwards to the
corresponding opening bracket character. For example, move the
cursor to the closing brace after the printf
line above
and press %
.
vi is even smart enough to find a bracket character
for you. If the cursor is not on a bracket character, when you press
%
, vi will search forward on the
current line to the first open or close bracket character it finds,
and then move to the matching bracket!
For instance, with the cursor on the >
in the
first line of the example above, %
will find the
open parenthesis, and then move to the close parenthesis.
Not only does this search character help you move forward and
backward through a program in long jumps, it lets you check the
nesting of brackets and parentheses in source code. For example, if you put
the cursor on the first {
at the beginning of a C function,
pressing %
should move you to the }
that (you think) ends the
function. If it's the wrong one, something has gone wrong somewhere.
If there is no matching }
in the file,
vi will beep at you.
Another technique for finding matching brackets is to turn on the following option:
:set showmatch
Unlike %
, setting showmatch
(or its abbreviation sm
) helps you
while you're in insert mode. When you type a )
or a
}
,[11]
the cursor will briefly move back to the matching (
or
{
before returning to your current position.
If the match doesn't exist, the terminal beeps.
If the match is merely off-screen, vi silently keeps going.
[11] In elvis, vim, and vile,
showmatch
also shows you matching square brackets ([
and]
).
The source code for a large C or C++ program will usually be spread
over several files. Sometimes, it is difficult to keep track of
which file contains which function definitions. To simplify
matters, a UNIX command called ctags
can be used together
with the :tag
command of vi.
NOTE: UNIX versions of
ctags
handle the C language, and often Pascal and Fortran 77. Sometimes they even handle assembly language. Almost universally, however, they do not handle C++. Other versions are available that can generate tags files for C++, and for other languages and file types.
The ctags
command is issued at the UNIX command line.
Its purpose is to create an information file
that vi can use later to determine which files define which
functions. By default, this file is called tags.
From within vi, a command of the form:
:!ctags file.c
will create a file named tags in your current directory that contains information on the functions defined in file.c. A command like:
:!ctags *.c
will create a tags file describing all the C source files in the directory.
Now suppose your tags file contains information on all the source files that make up a C program. Also suppose that you want to look at or edit a function in the program, but do not know where the function is. From within vi, the command:
:tagname
will look at the tags file to find out which file contains the definition of the function name. It will then read in the file and position the cursor on the line where the name is defined. In this way, you don't have to know which file you have to edit; you only have to decide which function you want to edit.
You can use the tag facility from vi's command
mode as well. Place the cursor on the identifier you wish to look
up, and then type ^]
. vi will
perform the tag lookup and move to the file that defines the identifier.
Be careful where you place the cursor; vi uses the
"word" under the cursor starting at the current cursor
position, not the entire word containing the cursor.
NOTE: If you try to use the
:tag
command to read in a new file and you haven't saved your current text since the last time you changed it, vi will not let you go to the new file. You must either write out your current file with the:w
command and then issue:tag
, or else type::tag!name
to override vi's reluctance to discard edits.
The Solaris 2.6 version of vi actually supports tag stacks. It appears, however, to be completely undocumented in the Solaris man pages. Because many, if not most, versions of UNIX vi don't do tag stacking, we have moved the discussion of this feature to Section 8.5.3, "Tag Stacks" in Chapter 8, where tag stacking is introduced.