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 tab | |
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 started | |
ia line with a tab followed by two space characters, then the spaces | |
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 try | |
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, and | |
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-D | |
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 just | |
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 with | |
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 present | |
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 some | |
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 or | |
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 the | |
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-D | |
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 writer | |
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 deeply | |
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 the | |
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 the | |
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 into | |
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 you | |
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 longer | |
ithan it needs to be. So if your solution seems long-winded, take a | |
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 your | |
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 spacing | |
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 for | |
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, quote | |
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 to | |
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 to | |
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 disable | |
iautoindent. When programming, I use a simple .exrc file containing | |
ian se ai bf nu sw=4 ts=4 wm=0 line. If I cut a section of indented | |
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 | |
Solutions | |
i | |
iNext Time | |
i | |
iThe next part of this tutorial will cover the :abbreviate and :map! | |
icommands, both of which help you save typing while you are in | |
itext-insertion submode. Then, on to the editor's several | |
ifacilities for creating macros and pseudo-macros that you can use | |
ifrom the command submode. And it will finish with more readers' | |
iquestions and my answers, if you readers will send me some | |
iworthwhile questions soon. | |
i | |
iYou may be laughing at that final word, "soon" | |
i | |
iBut my secret weapon this time is that, while my editor was away I | |
ispent some time writing about half of the next part. So, when she | |
ireturns from a week of well-deserved vacation in mid-November, I | |
iplan to have the completed tutorial installment waiting for her. | |
iWish me luck. | |
i | |
Part 9: Take Charge with Macros | |
Back to the index |