Introduction
Introduction Statistics Contact Development Disclaimer Help
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
You are viewing proxied material from gopher.black. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.