It's time to shift into high gear and round out our Vim skills with a more complete set of editing functions. We don't want to always have to enter Insert Mode to make changes to our code. Sometimes we need to move large chunks of code around, change a bunch of variable names, or delete irrelevant code. These code editing tasks would be painful if we had to do them all in Insert Mode, but they can be positively trivial with Vim's powerful editing commands.
The Only Constant is Change
For these editing commands, we'll use the same simple Ruby statistics code snippet we used in the last post. Here it is again for your copy-and-paste convenience:
module Statistics
def self.sum(data)
data.inject(0.0) { |s, val| s + val }
end
def self.mean(data)
sum(data) / data.size
end
def self.variance(data)
mu = mean data
squared_diff = data.map { |val| (mu - val) ** 2 }
mean squared_diff
end
def self.stdev(data)
Math.sqrt variance(data)
end
end
data = [1,2,2,3,3,3,3.5,3.5,3.5,4,4,4,5,5,6]
p Statistics.mean(data)
p Statistics.variance(data)
p Statistics.stdev(data)
For our first trick, let's learn some better ways to delete stuff than entering Insert Mode and holding down the <Delete> key. Let's try changing the variance method name again, but without entering Insert Mode. As before, type /ia to get to the right spot in 'variance'. Now type dw and voila, the end of the word disappears.What is actually happening is typing the d character starts the delete function, and typing the w character moves the cursor to the end of the word, deleting everything in its path. The general delete function is <repeat>d<movement>, and <movement> can be any movement we learned from the last post. That means d$ will delete everything from the cursor to the end of the line, db will delete backwards to the start of the word the cursor is on, and dj will delete the current line and the line below the cursor. That's right, the up and down movement keys delete whole lines—two of them, in fact. I don't use that one very often since it isn't easy to remember when you need it.
A better way to delete lines is to simply use dd. This command deletes whatever line the cursor is on. If you add a repeat number before it, it will delete that many lines starting at the line the cursor is on. Let's try it out by deleting line 23 in our code example, but first, let's finish our previous renaming. Type n.n to move to the next instance of 'variance', delete the end of it, and then move to the last instance. Notice that the delete function can be repeated by the repeat (.) command. Now that we're on the line that we want to be on, type dd to delete this entire line.
Pretty simple. Now you may be wondering if d has a dual command like the others we covered before, and indeed it does. A capital D will delete everything from the cursor to the end of the line. It's a shortcut for d$ that's a little easier to type. This shortcut turns out to be quite convenient when editing code. I should also mention that a cool movement that you can do at the end of the d command is a search. Yes, you can use search as a movement, so if you wanted to delete all of the '3.5' values from the data array on line 21, you could do it easily by moving to the first '3.5' and typing d/4. This sequence will delete everything from the cursor to the first instance of '4', and the search even happens while you're typing, just like normal, so you can see what's actually going to be deleted before you commit to it.
One last form of the delete function exists on the command line. You can type :<range>d to delete a number of lines specified by <range>. The range consists of two numbers, <first_line>,<last_line>, so :16,18d would delete the stdev method no matter where the cursor was when the command was executed. If you happen to be on the first line of a range that you want to type, you can also use . instead of the line number. Other special characters used in a <range> specifier are ^ and $, and in this context they refer to the beginning and end of the file, so :^,$d would delete the entire file! This is normally not recommended, but these special characters do come in handy for other commands that use a <range>. Otherwise, this delete method works well when you're deleting a large number of lines and don't want to move to the cursor or do the line number subtraction in your head.
Now that you know how to delete things so easily, you'll probably delete more than you intend to at times. Vim makes it easy to recover with u, which stands for undo. This will probably be your most used key after <Esc>, and it has been greatly improved since Vi's one-deep undo stack. Vim allows you to undo all of the edits done up until the file was opened. To redo an edit if you go back too far, use <Ctrl>-R. It's not as easy to type, and it's unfortunate that redo isn't U. U is taken up by a variant of the undo command that I never use because it breaks the undo stack. Just remember, u is undo and <Ctrl>-R is redo.
Getting back to the delete functions, delete doesn't just delete things. It does more. The characters that were deleted do not fully disappear. They're held in a temporary buffer so that you can plunk them down somewhere else if you want. The way to paste them back into the buffer is with the p command. The paste command will insert the characters in the delete buffer after the cursor if they were not whole lines, or insert the lines below the cursor if they were whole lines. Try it out by moving up two lines in our example and typing p. (This assumes you still have the line we deleted a while back with dd in the temporary buffer.) The line that you previously deleted will be inserted below the line of data values.
To paste the temporary buffer before the cursor, use P instead. What if you want to copy something instead of delete it? Well, then you want y instead of d. The y stands for yank, and it works very similarly to delete, except it doesn't actually delete anything, only puts it in the temporary buffer. The yy, Y, and :<range>y commands all work like their delete counterparts.
Still More Ways to Change
Vim has a number of other quick edit functions. If you want to change a word, you can use cw. So to change 'mean' to 'avg' in the file, move to an instance of 'mean' with /me. Then type cwavg<Esc> to make the change.
Finally, you can type n.n.n. to change the other occurrences of 'mean'. The c command is very similar to the d command as well, but in a different way than the y command is similar to the d command. The c command deletes characters just like d, but then it enters Insert Mode right away so you can enter something different in place of what was deleted. The cc, C, and :<range>c commands work like you would expect, but the :<range>c command is a little cumbersome because you have to enter the new text at the command line instead of in place.
Another variant of delete is x. This command deletes a single character, and it's reminiscent of the old days when you would back up and x-out characters on a typewriter. Still another way to delete is with the r command. The r stands for replace, and it will replace a single character with the next character typed. It's useful for simple typo corrections. For more major changes, you can use R to enter Replace Mode and overwrite as many characters as you need to with newly typed ones. Like Insert Mode, you have to exit with the <Esc> key.
Finally, the J command deletes the newline character at the end of the current line, joining the next line to the end of the current line no matter where the cursor is in the current line. These are all useful commands in certain situations, so be sure to get comfortable with them.
Shifting Gears (I mean text)
Let's move on from all of the ways Vim can delete stuff to something else that comes up a lot in programming: indenting. In Vim changing the indent level of a block of code is a snap. Use >> to shift a line right one indent level, and use << to shift a line left one indent level. A repeat number can be specified before the shifts to indent multiple lines. Let's try changing our code file to make the sum method private. Normally private methods go at the end of modules or classes, so we need to move it to the end of the list of methods and add private above it. I'll let you try this out on your own (hint: make use of gg, dd, p, and O to speed things up). Then position the cursor on the method declaration line and type 3>> to shift the method to the proper indentation.
Vim also has :<range>> and :<range>< commands to indent larger numbers of lines easily. To fix indentation by making it the same as whatever the indent of the previous line is, you can also use ==.
Another coding task that comes up again and again is search and replace. Vim has a concise command line tool for doing this task called substitute, and the format of the command is :<range>s/<search_regex>/<replace_text>/[g]. The g at the end is optional, hence the brackets, and when it is included every occurrence of the search regex will be replaced. Otherwise, only the first occurrence on each line will be replaced. This format may look ungainly at first, but once you get used to it, it is so much more convenient than messing around with search-and-replace dialog boxes in other editors. A simple way to turn all of the 'avg' occurrences back to 'mean' would be :1,$s/avg/mean/. That command substitutes the first occurrence of 'avg' on every line from line 1 to the last line with 'mean'. Having the search term be a regular expression makes this feature quite powerful.
More Power with Macros
Speaking of powerful features, let's take a moment to go over macros. Vim has this feature where you can record a set of keystrokes and assign them to any key to be replayed later. Any key can be used because the replay command is @<key>. I normally use the number keys for macros, and they are extremely useful when changing regularly structured data files. Let's do a simple example by making a macro to comment out the beginning of a line and move to the next line. That way it can be repeated to comment out larger blocks of code. Start by moving to line 13 in our example code. We'll be commenting out the stdev method. Type q1 to start the macro recording that will be saved in key 1. Notice that the bottom of the window should say 'recording'.
Now type ^i#<Esc>jq to perform the move, edit, move, and end the recording. To replay the recording two more times, type 2@1, and the next two lines are also commented out.
Maybe not the best way to comment out code lines (we'll cover a better way next time), but it shows the utility of macros. Where macros really shine is when you want to do a complicated edit to a file hundreds or thousands of times. With macros you can do things in Vim that would take more time and lines of code to do in any scripting language, even Ruby.
With that, I should wrap up this post. This is more than enough to work on, and once you get good at these commands, you'll be well on your way to editing at the speed of thought. The new commands we covered were:
- d, dd, D, :d - delete, delete line, delete to end, ranged delete
- u, <Ctrl>-R - undo and redo
- p, P - paste after or before cursor
- y, yy, Y, :y - yank, yank line, yank to end, ranged yank
- c - change
- x - delete character
- J - join next line to end of current line
- r, R - replace character, Replace Mode
- <<, >>, ==, :<, :> - left, right, match indent
- :s - substitute
- q, @ - record and replay macro
Keep in mind just because it's not covered here, doesn't mean it isn't possible to do efficiently in Vim. If you have a complicated editing task that you want to automate, chances are Vim can do it, even if it's not obvious from the commands you already know. Take a minute to google around and see if you can find a way to solve your problem in only a few keystrokes. As you discover more and more of these editing tricks, your proficiency in Vim will keep increasing, and you'll be amazed at how quickly you'll be able to get stuff done.
Next week we'll look at some great plugins for Vim that make it even more useful and efficient for coding. We'll also cover some simple buffer management commands for when you have multiple buffers open in one Vim session.
No comments:
Post a Comment