| iThe vi/ex Editor, Part 8: Indent, Like a Typewriter
i
i Automatic Indentation
i Backing off Indentation
i Juggling some :set options
i An Exercise for You
i Hard Tabs
i Enable and Disable autoindent
i Next Time
i
iAutomatic Indentation
i
iComputer editing is a great advance over typing on paper, the
iconsensus has it. But wouldn't you be happier yet if you had the
itabstop-setting capability of your old typewriter, too? With the
iVi and Ex editor, you have a feature that's just as powerful and a
ilot easier to use, but not many users know it's there. Yet there
iit is, on even early versions of the editor -- its name is
iautoindent.
i
iWith autoindent turned on, you can start a running indent any time
iyou are in text-insert mode -- whether initiated from an R or from
iany other command that starts you typing in text. Just put some
iwhitespace at the start of the first line you want indented. From
ithen on, each time you hit the return key, the editor will
iautomatically insert exactly the same amount of whitespace at the
istart of the next line. That is, if you begin a line with five
ispace characters, every following line you type in will begin with
ifive space characters also; causing the left margin to line up
inicely, but five spaces in from the normal margin setting.
i
iNote that I said "whitespace" above, which includes the tab
icharacter as well as the space character. Autoindentation works
iwhether you start a line with spaces, or tabs, or some combination
iof the two. In fact, it even understands redundant combinations,
isuch as starting a line with two space characters followed by a ta
icharacter.
i
iWe both know that tabbing from two spaces in will reach the first
itabstop, as surely as tabbing from the left margin would -- those
itwo spaces have no effect as far as where the indented line
iactually starts. Autoindentation knows this too, and will start
ieach subsequent line with just a tab character. But if you starte
ia line with a tab followed by two space characters, then the space
iwould have an effect -- moving the margin to the right two more
icharacter positions than the tab alone would have. In this case,
iautoindentation will incorporate those two following space
icharacters, as well as the leading tab, into the text at the start
iof each subsequent line.
i
iThe general rule is that while autoindentation will always put in
ithe same amount of leading whitespace that you did, or at least tr
ihard to do so, it may use its own discretion as to the combination
iof tab and space characters it uses to do this.
i
iIf you want to increase the indentation at some point, just type
imore whitespace at the beginning of an (already indented) line, an
iyour new indentation depth will be the rule from that point on.
iYou can even leave insert mode to correct a mistake without losing
ithe indented margin setting, providing you return to insert mode
iwith an o or O command.
i
iBacking off Indentation
i
iTo set the indentation back (or off), you need to use the Control-
icharacter. When you want to stop the indentation temporarily, for
ijust one or a few lines, type a circumflex (^) followed by
iControl-D at the start of each such line, to move the start of jus
ithat one line back to the left margin. If you type the numeral
izero followed by control-D at the start of a line, automatic
iindentation disappears completely until you again start a line wit
iwhitespace. This takes effect starting with the line you are on
iwhen you type it.
i
iTo set the indentation point back to the nearest shiftwidth
i(discussed below) stopping place that's to the left of your presen
iindent point, and leave it there until you change it again, type
ijust control-D at the start of the line. If that is not enough
imargin reduction for you, just type several consecutive control-D
icharacters to get the amount you want. This setback also takes
ieffect starting with the line you are on when you type the
icontrol-D.
i
iJuggling a few :set options
i
iAnd that brings up the whole vexed question of lengths of tabs and
ilineshifts, which are controlled by three options of the :set
icommand. When you are in the editor, type in the :set command
iquery as in the first line below, and see whether the response is
ithe default -- as given in the line following it:
i
i :se sw ts ht
i
i shiftwidth=8 tabstop=8 hardtabs=8
i
iThe first of these reflects the primary problem in using
iautoindentation. The shiftwidth option was created to control som
icommands I haven't discussed yet, which add or subtract whitespace
iat the start of each line you designate; this option sets the
inumber of spaces these commands add or subtract. In addition,
ithough, the value of this option also determines where your left
imargin will land when you go back part of the way to your window o
iscreen's left margin.
i
iSo if your shiftwidth option is set to the default value of eight
ispaces, as shown above, then there will be a stopping point every
ieight spaces across your screen or window -- in the ninth column,
iin the seventeenth column, in the twenty-fifth column, etcetera.
i(This presumes that you call the leftmost character position on
iyour window or screen column one, which is what the editor calls
iit, and not column zero.) So if your autoindented margin is in th
itwenty-first column, typing control-D at the start of a line will
iput it back to the seventeenth column. If the margin is presently
iin the eighteenth or the twenty-fourth column, the effect would be
ithe same. But if the present margin is in the twenty-seventh or
ithirtieth column, then a single control-D would set it back to
icolumn twenty-five.
i
iOf course, you can reset the shiftwidth value via the :set command
iMany programmers reset that value to four. Then the stop points
iwill be in every fourth column -- in column five, in column nine,
iin column thirteen, in column seventeen, and so on. This reduces
iline wrap in program source code with many levels of tab
iindentation.
i
iHere's a visual representation of the difference: first of the
idefault tab stops every eight columns, then as they are when reset
ito every four columns:
i
i +-------+-------+-------+-------+-...
i 1 9 17 25 33
i
i +---+---+---+---+---+---+---+---+-...
i 1 5 9 13 17 21 25 29 33
i
iBut you just might be creating a problem by doing this. With
iidentical shiftwidth and tabstop values, backing up via a control-
irequires only erasing one tab character or erasing one or more
ispace characters; never anything more complex. With a shiftwidth
ivalue of four and a tabstop value of eight, though, there will be
itimes when a control-D requires the editor to remove one tab from
ithe whitespace sequence with which it starts each line, and
isimultaneously add four space characters. A few versions of the
ieditor cannot handle this complexity in some circumstances, and
iwill at times put garbage in your file. Even more likely is that
ithe editor will mess up when it encounters tab characters in the
imiddle of lines.
i
iThe tabstop option controls the number of spaces the editor thinks
iyou want between tabstops. With this option at its default value
iof eight, there will be a tabstop every eight spaces, falling in
ithe same columns as the shiftwidth stop points when that option's
ivalue was also at its default value of eight. So if you set the
ivalues of both options to four, you will still have both options'
istop points falling in the same columns, solving the problem posed
iin the last paragraph.
i
iSolving it at quite a price, though. The editor can use your
ispecial value of four spaces between tabstops (or any other value
iyou choose to give) when it is inserting and removing tabs as you
itype, but it has no way to mark those characters in your file to
isay "This is a four-column tab character" and "That is an
ieight-column tab character". Not that there is any difference
ibetween the tab characters themselves. A tab always moves the
icursor to the next tabstop point in the line, wherever that may be
iThe difference is that some of your tabs will be inserted when you
iexpected the editor to find a tabstop point every four columns;
iothers when you (or someone else) were expecting tabstops every
ieight columns.
i
iSo when you set your tabs value to four and then edit a file that
iwas composed with tabs at their default value of eight,
iindentations will be only about half as deep as the original write
iintended they should be. And when you write this file back to
ipermanent storage, anyone who uses the file after you and has
idefault tab settings will find the indentations you added to be
iabout twice as deep as you intended -- this will often cause deepl
iindented lines to be too long to be displayed on a single line of
ithe user's screen or window.
i
iSince you've gotten this far in the tutorial, you're surely a
iskilled user who can see how to get around this -- by writing a
i.exrc file entry to translate eight-column-tab indentations into
ifour-column-tab equivalents as you pull in a file to edit, and a
imacro to do the reverse in the course of writing your work out to
ipermanent storage.
i
iAn Exercise for You
i
iIt was several tutorial parts ago that I last put exercises for th
ireader in the tutorial itself. This seems like a good place to
irevive that practice. Just how would you write a command sequence
ito handle that latter operation as regards start-of-line
iindentations? Let's say you edit in screen mode, with your tabstop
ioption set to a value of four, so that a ten-column indentation
iconsists of two tabs followed by two spaces and a thirteen-column
iindent is three tabs followed by one space. But when you write th
ifile to permanent storage, you want it to be in the conventional
iformat of eight columns between tab points (at least for
iindentations) -- so that same ten-column indentation will now
iconsist of just one tab followed by two spaces, and the
ithirteen-column indent will have a single tab followed by five
ispaces -- to keep the indentations at the same depth they were.
iWhat sequence of commands will accomplish this?
i
iTo simplify the problem, assume that the curly brace characters
i("{" and "}") never appear in files you edit (if they are present,
iwhich is common for program source code, choose another character
ipair) and that you will only be writing to the original file name,
ilet ^I stand for a real tab character when you write your answer,
iand don't worry about how you would turn your command sequence int
ia macro. But, definitely do remember that you will be doing a
iwrite in the middle of your editing session from time to time, to
iguard against losing work in a system crash, so your command
isequence must leave the file copy in the editor buffer just as it
iwas before you wrote the modified version to storage, ready for yo
ito continue editing.
i
iThis exercise is not so difficult if you've been following this
itutorial carefully. The biggest hazard for those readers is that
ithey may come up with a sequence that will work, but is much longe
ithan it needs to be. So if your solution seems long-winded, take
ilook at my hint before you jump to my solution.
i
iThese translator macros will work nicely for leading whitespace
i(indentations), but it would take incredibly complex scripts
i(whether Vi editor scripts or scripts for most other Unix
iutilities) to deal with tabs in the interiors of lines. The
ipestilential problem there is that you don't know just where an
iinterior tab character is placed -- how many positions in from the
istart of the line. For example, when you are trying to translate
ieight-column tabs into four-column equivalents, and your macro
ifinds a single eight-column tab in the middle of a line, is that
itab in a column that is five or more columns from the next tab
istopping point? If yes, it must be replaced by two of the
ifour-column tabs; if no, it is correct as it is. Similarly, when
igoing from four-column to eight-column tabs, a solitary tab in the
imiddle of a line may be left there or may have to be replaced by
ispace characters, depending on its column position.
i
iIf you must do this kind of translation, your best bets are the -e
iand -i options to recent versions of the pr Unix command. Running
ia file through this utility will make the conversions correctly,
ieven when the whitespace appears in the middle of lines. The
idownside is that your text may be reformatted to some degree.
i
iHard Tabs
i
iAnd then there is the hardtabs option to the :set command. That
ioption is used to tell the editor how far apart the tab stopping
ipoints are on your physical terminal -- the editor uses this
iinformation to decide what mix of tab and space characters will
irepresent on your screen the indentation depth that's in your file
iThat is, the editor runs its own translator program, if necessary,
ito make the spacings on your screen the same depth as those in you
ifile. Here too, any difference between this value and either of
ithe previous two is likely to cause problems. It's fortunate that
iany value you give to this option will be overridden by the spacin
ivalue that is in your Termcap or Terminfo file, because a
idifference between the terminal tab setting Vi expects and that
iwhich your terminal is actually using will scramble your screen fo
isure.
i
iSo my reluctant admonition to you is to leave all three of these
ioptions set at their default values of eight. Messing around with
iany of them is just too likely to cause trouble.
i
iEnable and Disable autoindent
i
iOf course, all this means that when you have autoindentation on,
ithe control-D, circumflex followed by control-D, and zero followed
iby control-D sequences are all metastrings at the beginning of an
iindented line. To turn the metavalue off, so you can put one of
ithese strings into the text at the start of an indented line, quot
iin the control-D character by preceding it with a control-V.
i
iSo how do you turn the whole autoindent mode on and off? It's
inormally off when you begin an editor session, and the usual way t
iturn it on is to use the :set command. Just type :se ai to turn
ithis feature on. When you want to tell the editor to stop
iautomatically indenting every time you start a line with
iwhitespace, type :se noai (from command mode) to turn autoindent
ioff again.
i
iAutoindent also works with the line-mode append insert commands,
iwhich can be abbreviated a i respectively. These commands let you
itype in new lines of text, below or above the current line,
irespectively. That is, they are generally the line-mode
iequivalents of the screen-mode o O commands. They can only be run
iwhen you are in line mode; even preceding one of them with a colon
i(":") will not let you run it from screen mode.
i
iThe setting of the autoindent option controls autoindentation
iwithin these text insertions, too, but there is also another way t
icontrol it that works only with these line-mode commands. Whenever
iyou follow one of these commands or its abbreviation with an
iexclamation point ("!"), without any characters or space in
ibetween, you toggle the autoindention setting for that insertion
ionly. That is, if autoindentation was off, the ! turns it on
iduring this insertion. Similarly, if autoindentation was on at the
itime, the ! turns it off just for this insertion.
i
i[Editor's Note : Here's an example where it really helps to disabl
iautoindent. When programming, I use a simple .exrc file containin
ian se ai bf nu sw=4 ts=4 wm=0 line. If I cut a section of indente
ilines from one window and paste it into my program I get a
istaircase effect as each line is inserted with one more tab than
ithe last. Most annoying.]
i |