The vi/ex Editor, Part 8: Indent, Like a Typewriter | |
Automatic Indentation | |
Backing off Indentation | |
Juggling some :set options | |
An Exercise for You | |
Hard Tabs | |
Enable and Disable autoindent | |
Next Time | |
Automatic Indentation | |
Computer editing is a great advance over typing on paper, the | |
consensus has it. But wouldn't you be happier yet if you had the | |
tabstop-setting capability of your old typewriter, too? With the | |
Vi and Ex editor, you have a feature that's just as powerful and a | |
lot easier to use, but not many users know it's there. Yet there | |
it is, on even early versions of the editor -- its name is | |
autoindent. | |
With autoindent turned on, you can start a running indent any time | |
you are in text-insert mode -- whether initiated from an R or from | |
any other command that starts you typing in text. Just put some | |
whitespace at the start of the first line you want indented. From | |
then on, each time you hit the return key, the editor will | |
automatically insert exactly the same amount of whitespace at the | |
start of the next line. That is, if you begin a line with five | |
space characters, every following line you type in will begin with | |
five space characters also; causing the left margin to line up | |
nicely, but five spaces in from the normal margin setting. | |
Note that I said "whitespace" above, which includes the tab | |
character as well as the space character. Autoindentation works | |
whether you start a line with spaces, or tabs, or some combination | |
of the two. In fact, it even understands redundant combinations, | |
such as starting a line with two space characters followed by a tab | |
character. | |
We both know that tabbing from two spaces in will reach the first | |
tabstop, as surely as tabbing from the left margin would -- those | |
two spaces have no effect as far as where the indented line | |
actually starts. Autoindentation knows this too, and will start | |
each subsequent line with just a tab character. But if you started | |
a line with a tab followed by two space characters, then the spaces | |
would have an effect -- moving the margin to the right two more | |
character positions than the tab alone would have. In this case, | |
autoindentation will incorporate those two following space | |
characters, as well as the leading tab, into the text at the start | |
of each subsequent line. | |
The general rule is that while autoindentation will always put in | |
the same amount of leading whitespace that you did, or at least try | |
hard to do so, it may use its own discretion as to the combination | |
of tab and space characters it uses to do this. | |
If you want to increase the indentation at some point, just type | |
more whitespace at the beginning of an (already indented) line, and | |
your new indentation depth will be the rule from that point on. | |
You can even leave insert mode to correct a mistake without losing | |
the indented margin setting, providing you return to insert mode | |
with an o or O command. | |
Backing off Indentation | |
To set the indentation back (or off), you need to use the Control-D | |
character. When you want to stop the indentation temporarily, for | |
just one or a few lines, type a circumflex (^) followed by | |
Control-D at the start of each such line, to move the start of just | |
that one line back to the left margin. If you type the numeral | |
zero followed by control-D at the start of a line, automatic | |
indentation disappears completely until you again start a line with | |
whitespace. This takes effect starting with the line you are on | |
when you type it. | |
To set the indentation point back to the nearest shiftwidth | |
(discussed below) stopping place that's to the left of your present | |
indent point, and leave it there until you change it again, type | |
just control-D at the start of the line. If that is not enough | |
margin reduction for you, just type several consecutive control-D | |
characters to get the amount you want. This setback also takes | |
effect starting with the line you are on when you type the | |
control-D. | |
Juggling a few :set options | |
And that brings up the whole vexed question of lengths of tabs and | |
lineshifts, which are controlled by three options of the :set | |
command. When you are in the editor, type in the :set command | |
query as in the first line below, and see whether the response is | |
the default -- as given in the line following it: | |
:se sw ts ht | |
shiftwidth=8 tabstop=8 hardtabs=8 | |
The first of these reflects the primary problem in using | |
autoindentation. The shiftwidth option was created to control some | |
commands I haven't discussed yet, which add or subtract whitespace | |
at the start of each line you designate; this option sets the | |
number of spaces these commands add or subtract. In addition, | |
though, the value of this option also determines where your left | |
margin will land when you go back part of the way to your window or | |
screen's left margin. | |
So if your shiftwidth option is set to the default value of eight | |
spaces, as shown above, then there will be a stopping point every | |
eight spaces across your screen or window -- in the ninth column, | |
in the seventeenth column, in the twenty-fifth column, etcetera. | |
(This presumes that you call the leftmost character position on | |
your window or screen column one, which is what the editor calls | |
it, and not column zero.) So if your autoindented margin is in the | |
twenty-first column, typing control-D at the start of a line will | |
put it back to the seventeenth column. If the margin is presently | |
in the eighteenth or the twenty-fourth column, the effect would be | |
the same. But if the present margin is in the twenty-seventh or | |
thirtieth column, then a single control-D would set it back to | |
column twenty-five. | |
Of course, you can reset the shiftwidth value via the :set command. | |
Many programmers reset that value to four. Then the stop points | |
will be in every fourth column -- in column five, in column nine, | |
in column thirteen, in column seventeen, and so on. This reduces | |
line wrap in program source code with many levels of tab | |
indentation. | |
Here's a visual representation of the difference: first of the | |
default tab stops every eight columns, then as they are when reset | |
to every four columns: | |
+-------+-------+-------+-------+-... | |
1 9 17 25 33 | |
+---+---+---+---+---+---+---+---+-... | |
1 5 9 13 17 21 25 29 33 | |
But you just might be creating a problem by doing this. With | |
identical shiftwidth and tabstop values, backing up via a control-D | |
requires only erasing one tab character or erasing one or more | |
space characters; never anything more complex. With a shiftwidth | |
value of four and a tabstop value of eight, though, there will be | |
times when a control-D requires the editor to remove one tab from | |
the whitespace sequence with which it starts each line, and | |
simultaneously add four space characters. A few versions of the | |
editor cannot handle this complexity in some circumstances, and | |
will at times put garbage in your file. Even more likely is that | |
the editor will mess up when it encounters tab characters in the | |
middle of lines. | |
The tabstop option controls the number of spaces the editor thinks | |
you want between tabstops. With this option at its default value | |
of eight, there will be a tabstop every eight spaces, falling in | |
the same columns as the shiftwidth stop points when that option's | |
value was also at its default value of eight. So if you set the | |
values of both options to four, you will still have both options' | |
stop points falling in the same columns, solving the problem posed | |
in the last paragraph. | |
Solving it at quite a price, though. The editor can use your | |
special value of four spaces between tabstops (or any other value | |
you choose to give) when it is inserting and removing tabs as you | |
type, but it has no way to mark those characters in your file to | |
say "This is a four-column tab character" and "That is an | |
eight-column tab character". Not that there is any difference | |
between the tab characters themselves. A tab always moves the | |
cursor to the next tabstop point in the line, wherever that may be. | |
The difference is that some of your tabs will be inserted when you | |
expected the editor to find a tabstop point every four columns; | |
others when you (or someone else) were expecting tabstops every | |
eight columns. | |
So when you set your tabs value to four and then edit a file that | |
was composed with tabs at their default value of eight, | |
indentations will be only about half as deep as the original writer | |
intended they should be. And when you write this file back to | |
permanent storage, anyone who uses the file after you and has | |
default tab settings will find the indentations you added to be | |
about twice as deep as you intended -- this will often cause deeply | |
indented lines to be too long to be displayed on a single line of | |
the user's screen or window. | |
Since you've gotten this far in the tutorial, you're surely a | |
skilled user who can see how to get around this -- by writing a | |
.exrc file entry to translate eight-column-tab indentations into | |
four-column-tab equivalents as you pull in a file to edit, and a | |
macro to do the reverse in the course of writing your work out to | |
permanent storage. | |
An Exercise for You | |
It was several tutorial parts ago that I last put exercises for the | |
reader in the tutorial itself. This seems like a good place to | |
revive that practice. Just how would you write a command sequence | |
to handle that latter operation as regards start-of-line | |
indentations? Let's say you edit in screen mode, with your tabstop | |
option set to a value of four, so that a ten-column indentation | |
consists of two tabs followed by two spaces and a thirteen-column | |
indent is three tabs followed by one space. But when you write the | |
file to permanent storage, you want it to be in the conventional | |
format of eight columns between tab points (at least for | |
indentations) -- so that same ten-column indentation will now | |
consist of just one tab followed by two spaces, and the | |
thirteen-column indent will have a single tab followed by five | |
spaces -- to keep the indentations at the same depth they were. | |
What sequence of commands will accomplish this? | |
To simplify the problem, assume that the curly brace characters | |
("{" and "}") never appear in files you edit (if they are present, | |
which is common for program source code, choose another character | |
pair) and that you will only be writing to the original file name, | |
let ^I stand for a real tab character when you write your answer, | |
and don't worry about how you would turn your command sequence into | |
a macro. But, definitely do remember that you will be doing a | |
write in the middle of your editing session from time to time, to | |
guard against losing work in a system crash, so your command | |
sequence must leave the file copy in the editor buffer just as it | |
was before you wrote the modified version to storage, ready for you | |
to continue editing. | |
This exercise is not so difficult if you've been following this | |
tutorial carefully. The biggest hazard for those readers is that | |
they may come up with a sequence that will work, but is much longer | |
than it needs to be. So if your solution seems long-winded, take a | |
look at my hint before you jump to my solution. | |
These translator macros will work nicely for leading whitespace | |
(indentations), but it would take incredibly complex scripts | |
(whether Vi editor scripts or scripts for most other Unix | |
utilities) to deal with tabs in the interiors of lines. The | |
pestilential problem there is that you don't know just where an | |
interior tab character is placed -- how many positions in from the | |
start of the line. For example, when you are trying to translate | |
eight-column tabs into four-column equivalents, and your macro | |
finds a single eight-column tab in the middle of a line, is that | |
tab in a column that is five or more columns from the next tab | |
stopping point? If yes, it must be replaced by two of the | |
four-column tabs; if no, it is correct as it is. Similarly, when | |
going from four-column to eight-column tabs, a solitary tab in the | |
middle of a line may be left there or may have to be replaced by | |
space characters, depending on its column position. | |
If you must do this kind of translation, your best bets are the -e | |
and -i options to recent versions of the pr Unix command. Running | |
a file through this utility will make the conversions correctly, | |
even when the whitespace appears in the middle of lines. The | |
downside is that your text may be reformatted to some degree. | |
Hard Tabs | |
And then there is the hardtabs option to the :set command. That | |
option is used to tell the editor how far apart the tab stopping | |
points are on your physical terminal -- the editor uses this | |
information to decide what mix of tab and space characters will | |
represent on your screen the indentation depth that's in your file. | |
That is, the editor runs its own translator program, if necessary, | |
to make the spacings on your screen the same depth as those in your | |
file. Here too, any difference between this value and either of | |
the previous two is likely to cause problems. It's fortunate that | |
any value you give to this option will be overridden by the spacing | |
value that is in your Termcap or Terminfo file, because a | |
difference between the terminal tab setting Vi expects and that | |
which your terminal is actually using will scramble your screen for | |
sure. | |
So my reluctant admonition to you is to leave all three of these | |
options set at their default values of eight. Messing around with | |
any of them is just too likely to cause trouble. | |
Enable and Disable autoindent | |
Of course, all this means that when you have autoindentation on, | |
the control-D, circumflex followed by control-D, and zero followed | |
by control-D sequences are all metastrings at the beginning of an | |
indented line. To turn the metavalue off, so you can put one of | |
these strings into the text at the start of an indented line, quote | |
in the control-D character by preceding it with a control-V. | |
So how do you turn the whole autoindent mode on and off? It's | |
normally off when you begin an editor session, and the usual way to | |
turn it on is to use the :set command. Just type :se ai to turn | |
this feature on. When you want to tell the editor to stop | |
automatically indenting every time you start a line with | |
whitespace, type :se noai (from command mode) to turn autoindent | |
off again. | |
Autoindent also works with the line-mode append insert commands, | |
which can be abbreviated a i respectively. These commands let you | |
type in new lines of text, below or above the current line, | |
respectively. That is, they are generally the line-mode | |
equivalents of the screen-mode o O commands. They can only be run | |
when you are in line mode; even preceding one of them with a colon | |
(":") will not let you run it from screen mode. | |
The setting of the autoindent option controls autoindentation | |
within these text insertions, too, but there is also another way to | |
control it that works only with these line-mode commands. Whenever | |
you follow one of these commands or its abbreviation with an | |
exclamation point ("!"), without any characters or space in | |
between, you toggle the autoindention setting for that insertion | |
only. That is, if autoindentation was off, the ! turns it on | |
during this insertion. Similarly, if autoindentation was on at the | |
time, the ! turns it off just for this insertion. | |
[Editor's Note : Here's an example where it really helps to disable | |
autoindent. When programming, I use a simple .exrc file containing | |
an se ai bf nu sw=4 ts=4 wm=0 line. If I cut a section of indented | |
lines from one window and paste it into my program I get a | |
staircase effect as each line is inserted with one more tab than | |
the last. Most annoying.] | |
Solutions | |
Next Time | |
The next part of this tutorial will cover the :abbreviate and :map! | |
commands, both of which help you save typing while you are in | |
text-insertion submode. Then, on to the editor's several | |
facilities for creating macros and pseudo-macros that you can use | |
from the command submode. And it will finish with more readers' | |
questions and my answers, if you readers will send me some | |
worthwhile questions soon. | |
You may be laughing at that final word, "soon" | |
But my secret weapon this time is that, while my editor was away I | |
spent some time writing about half of the next part. So, when she | |
returns from a week of well-deserved vacation in mid-November, I | |
plan to have the completed tutorial installment waiting for her. | |
Wish me luck. | |
Part 9: Take Charge with Macros | |
Back to the index |