Ex-Mode Addressing
Whenever you want to give an editor command that will operate on text that’s already in the file you’re editing — to delete some text, change lower-case letters to capitals, write to a file, etcetera — the editor needs to know what part of the file to go to work on. A few commands have their addresses built in, and most ex-mode commands have default addresses that the editor will use if you don’t give an address, but that still leaves a lot of occasions where you need to know how to give the editor an address and what address to give.
Many ex-mode commands are almost identical to corresponding commands in normal mode; many more do similar things in different ways. Most of the benefit of these duplicative command sets comes from the totally-different addressing styles of ex and normal modes. The differing address concepts mean that an edit that would be difficult or impossible to do with one mode’s available addresses can be a piece of cake with an address form found in the other mode.
Since I mention “ex mode” so often, you may wonder whether there really is a separate mode for ex editing. There surely is — instead of filling your screen with text from the file you’re editing, this mode gives you a colon (:) prompt for your ex mode commands, and prints only an occasional line from the file on your screen. The feel of this mode is very much like giving UNIX commands from your shell prompt. Few people work in ex mode these days, largely because you can give most ex-mode commands from normal mode, but you can’t give any normal-mode commands while you are in ex mode. Or perhaps they just prefer the comfortable WYSIWYG feeling of seeing the text on screen, with changes appearing as they are made.
But there are times when you will need to work temporarily in ex mode. To get to ex mode when you first launch the editor, invoke it by typing ex instead of vim. To go to ex mode when you are already in the editor’s normal mode, enter Q. To get back to normal mode, type visual followed by a carriage return.
Wondering why I didn’t put a colon in front of that command to return to normal mode, which is obviously an ex-mode command? Because you don’t need to type that colon when you’re giving a command from within ex mode. It may even be harmful; the rule is that if you type a colon at the start of a command from within ex mode, there must be nothing between the colon and the command name or abbreviation. Not an address, not even a space, nothing at all.
So from this point on, I will display ex-mode commands without an initial colon, because you now know enough to type that colon only if you’re working in normal mode. And I’ll leave off the tag at the end of an ex-mode command that reminds you to finish with a carriage return because you now realize that any ex-mode command, given from either ex or normal mode, has to end with a carriage return.
Some of you may ask why I show ex-mode command lines in long-winded form, with spelled-out command names and lots of whitespace instead of using abbreviations. For instance, the two command lines:
global /^/ move 0
g/^/m0
are identical in their effect, and the second is surely faster to type, so why do I show the first form? Because the long version is much easier to follow when I’m demonstrating a new concept, and almost everything here will be new to at least some of you. And it’s a good idea to get to know the long forms, because you’ll soon be learning to write editor scripts, and those scripts would be as cryptic as APL to future maintenance programmers if you wrote them in terse style. When I go over the roster of ex-mode commands, I’ll tell you both the long name and one or two short names for each.
Ex-Mode Address
Single Addresses
A single address is often all you need with an ex-mode command. One address refers to just one line, which tells a command like delete or substitute to operate on that one line only. A command like insert or read, which puts something immediately before or after a particular line, has no use for more than one address.
A search pattern, as discussed in the first installment of this tutorial, is always an acceptable ex-mode address. You put the address at the start of the command line, before the command name (but after the initial colon if you are giving the command from normal mode), so:
?the [cC]at? delete
will erase the last previous line that contains the string “the cat” or “the Cat”, while:
/^GLOSSARY$/ read gloss.book
puts the contents of the file “gloss.book” right after the next line in the file you’re editing that contains only the word “GLOSSARY”.
There are two shorthand forms for reusing search patterns as addresses. Typing ?? or // tells the editor to use the last search pattern you used previously, and your choice of ?? or // will set the direction of the search, overriding the direction you chose the previous time you used that search pattern. That is, if you type:
?the cat? yank
// delete
?? print
the second command will search forward, to remove the last previous line containing the string “the cat”, even though your original use of that pattern was in a backward search. The third command will search backward to find the line to print, which (by coincidence) is the direction of the original search.
But the search pattern that those preceding abbreviations reuse may not be a pattern you used to search for a line. If you ran a substitute command after any pattern searches for lines, then the pattern you gave the substitute command to tell it what text to take out of the line is the pattern that will be reused. This is so even if your substitute command began with a search pattern to specify the line on which the substitution was to be performed — the search to find the pattern to be replaced within the line was run after the first search pattern had found the line to operate on, so the search within the line was the last pattern search run. So if you were to type:
/the cat/ substitute /in the hat/on the mat
?? delete
the second command would, in this case, delete the last previous line containing “in the hat”. To be sure that the pattern that gets reused is the last one used to find a line, use the abbreviations \? and \/ to search backward and forward, respectively. In all other respects these work just as typing ?? and // do.
Numeric Addresses
A line number is also a valid ex-mode address. The editor automatically numbers each line in the file consecutively, and this numbering is dynamic — that is, whenever you add or delete lines somewhere, the editor renumbers all the lines following the insertion or deletion point. So if you change some text on line 46 in your file, and then delete lines 11 and 12, the line with the text you changed is now line 44. And if you then add ten new lines after line 17, the line with your changed text on it now automatically becomes line 54.
There is never a gap or an overlap in the line number sequence, so the nth line in the file is always line number n ; that is, the 7th line is always line number 7, and so on. (There are several ways to display these line numbers, which I will expound in a later tutorial installment.) To delete the 153rd line in your file, just type:
153 delete
You don’t use any delimiters around a line number, or around any other address except a search pattern.
There are two symbolic line numbers and one fictional one that can be used in ex-mode addresses. As long as there are any lines in the buffer (that is, you haven’t specified a not-yet-existent file to edit and failed to enter any text so far), the editor regards you as being ‘on’ one of them, usually the last line affected by your latest command. A period or dot (.) is the symbolic address for this line. The last line in the file also has a symbolic address: the dollar sign ($). So if you should type:
. write >> goodlines
$ delete
the first command would append a copy of just the line you are on now to a file named “goodlines”, while the second would delete the last line in the file you are editing.
A few commands put text immediately after the line address you give: the append command is one of them. In order to let them put their text at the very start of a file (if that is where you want it), these commands can take the fictitious line number zero (0) as their address. So, if you want to type some text that will appear ahead of anything already in the file, you can do it with either of these command lines:
1 insert
0 append
insert and append are among the few ex-mode commands that cannot be run from normal mode by starting with a colon, because they occupy more than one line including the text to be put in. |
Marked Line Addresses
Writing your own line addresses is possible, too. You can attach lower-case letters to lines as line addresses, and change the attachments whenever you like. You can even use a special address that is automatically attached to the last line you jumped off from.
There are ways to mark a particular line with a lower-case letter of the alphabet, and those ways differ between ex and normal modes. I’ll be explaining all these ways in later installments of this tutorial. But once a line is marked, the ex-mode address that refers to that line is just the single-quote character followed immediately by the lower-case letter with which the line was marked. So typing:
'b print
will display on the screen whatever line you have previously marked with the letter b, no matter where the line is in relation to where you are when you give the command. No need to tell the editor whether to search forward or backward; there can be only one line at a time marked with any one letter, and the editor will find that line regardless.
The editor does some line marking on its own, too. Whenever you move from one line to another by a non-relative address, the editor marks the line you just left. (A non-relative address is one that isn’t a known number of lines from where you were.) So:
$
/the cat/
358
?glossary? +7
'b
are all non-relative addresses, and if you give any one of them, the editor will mark the line you are leaving for future reference. Then you can return to that line just by typing two successive single quotes:
''
as an ex-mode address. In theory, you can use this address with any ex-mode command. But it is so difficult to know for sure when you left a line via a non-relative address that this address form is best saved for going back to where you were when a mistake moves you far away, at least until you’re a wizard with this editor.
Address Offsets
Modifying any of these addresses is possible, and there are two ways to do this. The simpler way is to offset the address a certain number of lines forward or backward with plus ( + ) or minus (-) signs. The rule is that each plus sign following an address tells the editor to go one line farther forward in the file than the basic address, while each minus sign means a line backward. So these three addresses all refer to the same line:
35
37 --
30 +++++
Not that you’re likely to want to modify line-number addresses with counts, unless you’re weak in arithmetic and want the editor to do the adding and subtracting for you. But the count offsets will work with any ex-mode addresses, and are most often used with search patterns. In any event, there is a shorthand for these counts, too. A plus or minus sign immediately followed by a number (single or multiple digits) is equivalent to a string of plus or minus signs equal to that number, so that these two addresses are the same:
/^register long/ ++++
/^register long/ +4
The “4” in the second example does not mean “line number 4”, as it would if it appeared by itself as an address. After a plus or minus sign, a number is a count forward or backward from where the primary address lands (or if there is no primary address before the count, from the line you are on when you run the command). This is also one of the few places in ex-mode commands where you may not insert a blank space. The number must start in the very next character position after the plus or minus sign. If you violate this rule, the editor will uncomplainingly operate on some line that definitely is not the line you expected. |
The second style of address modifier is used where you want to do a search that’s complex. Let’s say you want to go forward in the file to delete a line that starts with “WARNING!”, but not the first such line the editor would encounter; you want the second instance. Either of these command lines will do it:
/^WARNING!/ /^WARNING!/ delete
/^WARNING!/ // delete
The consecutive search patterns tells the editor to find the location of the first pattern in the usual way, then start searching from that location for the second pattern. In this case, the first search pattern turned up the first instance of a line starting with “WARNING!”, and the second search pattern led the editor on to the second instance.
A very significant point here is that this combination of two search patterns, either of which could be a line address in itself, does not tell the editor to delete two lines. The sequence means that the first pattern is merely a way station, and that the single line found by the second search pattern is the only line to be deleted. In brief, what looks like addresses for two lines is actually only an address for one.
But that’s just the start of what you can do. You are not restricted to just two addresses. I’ve used up to ten of them to reach one specific line. As an example:
?^Chapter 3$? /^Bibliography$/ /^Spinoza/ /Monads/
will bring me to the title line of Spinoza’s first work with “Monads” in the title, in the bibliography for Chapter 3.
Nor are you limited to search pattern addresses when putting together address string. If you want to reach the first line following line 462 that contains the word “union”, typing:
462 /\<union\>/
will bring you there. And any of the addresses can take numerical offsets, so:
462 +137 /register int/ ---
is also a legitimate address string.
Even though an explicit +137 was used there to show that it will be added to 462, successive numeric addresses are additive in Vim. So this is the same as the last command:
462 137 /register int/ ---
Indeed, the awkward looking --- demonstrates this well. Each - is effectively -1 so --- is the same as -1 -1 -1 .
Semicolon Separators
There is another way to chain several addresses together to identify a single line in Vim — by separating the addresses with a semicolon.
Using addr ; sets the cursor to the line where addr matches before considering the subsequent address. Using:
/pat1/ /pat2/
will find the next line forward containing "pat2" after the next line forward (from the cursor) containing "pat1", whereas:
7 ; /pat2/
will find the next line forward from line 7 containing "pat2", leaving the cursor in line 7.
Addressing a Section of Text
Often you will want an ex-mode command to act on a series of successive lines. For example, you may want to delete a stretch of text or move it from one place to another. To do this, you give the address of the first line you want the command to act on, followed by the last line it should act on, and separate the two addresses with a comma or semicolon. So, the command:
14 , 17 delete
will delete line 14 and line 15 and line 16 and line 17.
Whether two addresses are separated by a comma or a semicolon changes the meaning radically.
With , the subsequent search starts from the current cursor position (and does not move the cursor, as ; does).
As with the single line usage of addr ; the cursor is set to the line where addr matches before considering the subsequent address.
Assuming the cursor is currently on line 3 of the following famous rhyme:
1 2 3 4 5 6 7 8 | Write a line of source code an edit full of Vim,
Four and twenty buffers opened on a whim.
When the make was issued the buffers tested clean,
Oh switch between your code and shell easily with screen.
The nerd was on his pimped out box cranking out the gems,
The boss was in his office thinking of his Benz.
The team were heading out the door and gave a little wave
When up came a buffer that had to be saved!
|
what do you think:
2 ; /was/ print
will print?
Four and twenty buffers opened on a whim.
When the make was issued the buffers tested clean,
The subsequent “was” after line 2 (where the next search starts from because ; resets the cursor position) resides on line 3.
Had the command been (again, with the cursor on line 3):
2 , /was/ print
it would have printed:
Four and twenty buffers opened on a whim.
When the make was issued the buffers tested clean,
Oh switch between your code and shell easily with screen.
The nerd was on his pimped out box cranking out the gems,
because the subsequent “was” from (after) line 3 (where the cursor is (it wasn’t moved, after all)) resides on line 5.
Any ex-mode addresses may be used with a comma or semicolon. All of the following combinations make sense:
'd ; /^struct/
257 , .
?^Chapter 9$? , $
The first address combination would cause the command that follows it to operate on the section starting with the line you have previously marked “d” and ending with the next forward (from mark “d”) line that begins with “struct”, inclusive. The second combination covers line 257 through the line you are on now. The third goes backward to include the previous line containing only “Chapter 9”, and forward to include the very last line in your file; plus all the lines in between, of course.
Line Order of Range Addresses
Vim prefers the second address in a separated range addresses to be farther ahead in the file than the first address. Vim will ask for confirmation to swap both sides and continue if this is not the case. So, the range:
57 , 188 delete
is just fine, while the similar-looking command:
188 , 57 delete
will result in the request to swap the addresses. You can prevent Vim from asking and let it swap the addresses automatically by prepending silent to the start of the ex range, as in:
silent 188 , 57 delete
If the two addresses happen to evaluate to the same line, the command will silently operate on the one line you’ve specified.
Chained Range Address Ends
Multiple space separated addresses can be chained together to achieve your desired range. For example, in:
/^INDEX$/ /^Xerxes/ , $ write tailfile
?^PREFACE$? /^My 7th point/ , ?^PREFACE$? /^In summary/ -- delete
the first command would write the latter part of the index to a new file, while the second could be used to remove a section of a book’s preface.
However, if you want the search after the comma to begin from the point the first search found, use a semicolon instead of a comma, as in:
?Stradivarius? ; /Guarnerius/
A note about default addresses
I’ve already mentioned that most ex-mode commands that can take an address have a “default” address built in, which tells the editor where to run the command if you don’t give an address with it. Each command has its own default address, which may be the current line, the current line plus the one following, the last line of the file, or the entire file.
The range separators have default addresses of their own. They are the same regardless of what command is being used, and they override any command’s own default address. If you put a comma or semicolon before a command and don’t put an address before the it, by default the address there is the current line. In the same way, if you leave out the address after it, the default there is also the current line. You can even leave out the address in both places and use the current-line default in both: that means the implied address is “from the current line to the current line”, which makes the current line the only line the command will operate on. So every one of the following command lines:
. write >> goodlines
. , . write >> goodlines
, . write >> goodlines
. , write >> goodlines
, write >> goodlines
will do exactly the same thing: append a copy of just the current line in the file you’re editing to another file named “goodlines”.
The exact same behaviour would have been observed with the semicolon separator instead of the comma in these examples because the explicit virtual line . and the implicit current line used by the write command are all referring to the “current line” (which is where semicolon and comma differ). |
Finally, there is one special symbol that represents the 1,$ (comma or semicolon-separated) address combination — the percent sign (%). Both refer to the entire file.
Now You Try It
Before you try the complex aspects of ex-mode addresses in actual editing situations, here are some problems you can build yourself up on. For each problem I’ve included a solution that will work fairly efficiently.
Exercise 2.1
How can you tell the editor to delete the line that holds the very last instance of “EXPORT” in your file? Be sure to check for cases where “EXPORT” appears on the last line of the file too. The solution is straightforward once you know where to start searching.
Exercise 2.2
Suppose you want to delete the very first line in the file with “EXPORT” on it, and that just might be line 1. Show two different ways to do this with ranges in Vim.
Exercise 2.3
If you use the address ?abc? , /xyz/ , it includes the two lines the searches (for “abc” and “xyx”) find, as well as all the lines between them. How would you specify that you want the affected lines to go up to, but not include, the lines the two searches find? In this case the solution is simpler than you might think.
Coming Up Next
The next installment of this tutorial will deal with the Global commands — they’re just too much to absorb right after the mind-numbing collection of address forms we’ve just gone through. And to give you more scope for using all these address forms, I’ll also cover ex-mode commands themselves, particularly the ones that have more capabilities than you suspect.