start page | rating of books | rating of authors | reviews | copyrights

Book HomeLearning the vi EditorSearch this book

7.5. Editing Program Source Code

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.

7.5.1. Indentation Control

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.[34]

[34]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.[35]

[35]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.

7.5.2. A Special Search Command

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.[36] For example, if you were to move the cursor to the first ( in:

[36]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 },[37] 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.

[37]In elvis, vim, and vile, showmatch also shows you matching square brackets ([ and ]).

7.5.3. Using Tags

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:

:tag name

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, where tag stacking is introduced.



Library Navigation Links

Copyright © 2003 O'Reilly & Associates. All rights reserved.