Foreword
I tried very hard to make the title short. Obviously, I didn’t quite make it.
The Stuff
Before Vim, there was darkness. Well, actually, there was a whole hodgepodge of text editors I used for programming. This includes Netbeans (for Java), a brief affair with UltraEdit, and on one regrettable night, Notepad. I discovered Vim in my second year at the University of Waterloo at the insistence of a friend and a computer science professor. I sharply recall muddling my way through vimtutor, struggling to remember the strange new keystrokes that seemed unintuitive at the time. (Why y for yank instead of c for copy?) However, after a few weeks and some regular expressions later, I confessed true love and haven’t looked back since.
Now, although vimtutor is a real handy introduction, I have since found out that Vim is so, so very much more. Here is a list of some neat Vim features that (probably) aren’t in vimtutor that I wish I knew about back when I first started using vim.
Increment and Decrement Numbers
Used in normal mode, CTRL-A increments and CTRL-X decrements the first numerical value on the line. Consider the following file:
This is file #7!
Open the file and type CTRL-A. You’ll see that #7 turned into #8. But, try the same in this file:
This is file #07!
#07 turns into #010 instead. This is because Vim considers any number preceded by 0 to be octal (unless it contains an 8 or 9). Similarly, any number prefixed by 0x is considered hexadecimal. If you wish to change this behaviour and increment 07 to 08, you need to change the nrformats setting, which specifies what Vim considers a numerical value. Type the following to see your nrformats setting:
:set nrformats
By default, nrformats should be set to octal,hex. To avoid 07 being understood as an octal number:
:set nrformats=hex
Now, 07 should be incremented to 08. It may intrigue you to know that another possible value for nrformats is alpha. If included, Vim will increment or decrement single alphabetic characters–quite handy when creating lists with alphabetical indices such as a), b), etc.
Record and Reuse Complex Actions
Typed in normal mode, q allows you to record complex actions and save them in a register for later use. It’s very useful in situations where regular expressions cannot handle the job, or where it could, but would be too complicated. This is easier to explain with an example, so let us consider the following situation.
You have an XML parsing application that parses XML files in the form of:
<batch>
<document docid="00001" \>
<document docid="00002" \>
</batch>
You would like to test your application with a large input file that has a thousand documents. You could write a quick script to generate this file; however, there is no need. Vim can do this for you! First, :set nrformats=hex. Then, starting with the above file, type the following sequence of keystrokes in normal mode:
ggjjqryyp<CTRL-A>q500@r
Wow, what just happened? It’s like magic, right? This is what you did.
- gg – Go to top of file
- jj – Go two lines down
- qr – Begin recording actions, storing them in the r register
- yyp<CTRL-A> – Copy the current line and paste it, then increment the number in the new line. (This is the action you are recording!)
- q – Stop recording
- 998@r - Replay the actions at register r, 998 times
Basically, by typing qr in normal mode, you begin to record every keystroke you make into register r until you type q in normal mode again. Typing @r will play the recorded keystrokes. There are, of course, many other registers than r. You can replace r with any number between 0-9, any upper or lowercase alphabet, or " (that is, [0-9a-zA-Z"] in regex). I personally like to use the q register, so I type qq to start recording and @q to use the action.
Split Windows
Nowadays, most people have nice, large monitors that can support much more than the 80 character columns of the screens of old. To take full advantage of this wonderful hardware, split your Vim windows! Try this:
:vnew
Vim should have opened a new file buffer by splitting the screen in half. Neat, right? You can also split screen horizontally with :new. If you want edit the same buffer in the new window, you can do this with :vsplit and :split. (This is often handy when you have a large file and you want to look at two different places at once.)
You will notice that when Vim splits a window vertically, it opens the new window to the left of the old one. Personally, this behaviour bothered me. If you think likewise, you can add the following to your .vimrc:
set splitright
Similarly, horizontal windows appear above the old window, and this can be changed by :set splitbelow.
In order to navigate between the windows, try the following commands:
- CTRL-W CTRL-W - Go to the next window; if you do this repeatedly, it will cycle through all the windows on the screen
- CTRL-W h - Go to the window left of the current one
- CTRL-W l - Go to the window right of the current one
- CTRL-W k - Go to the window above the current one
- CTRL-W j - Go to the window below the current one
When windows first open, Vim will distribute the screen real estate evenly between the old and new. However, you may want to change this around afterwards. Here are some useful commands:
- CTRL-W _ - Give all vertical space to the current window; e.g. vertical expansion
- CTRL-W | - Give all horizontal space to current window; e.g. horizontal expansion
- CTRL-W = - Evenly distribute space for all windows
- CTRL-W + - Increase current window height
- CTRL-W - - Decrease current window height
- CTRL-W > - Increase current window width
- CTRL-W < - Decrease current window width
I use the first three commands in this list frequently still, but there are better ways to fiddle with the window height and width--I'll talk about this in section Using the Mouse. For more on windows, try :help windows.
Tabbed Windows
Although Vim has buffers for editing multiple files, I prefer a more visual way of interacting with my opened files. Thus, I use tabbed windows, a feature supported by Vim since version 7. Here is a quick cheat sheet of commonly used commands:
- :tabnew - Open a new tab window
- :tabe - Used like :e, but opens in a new tab
- CTRL-<PgUp> - Go to the next tab on the right
- CTRL-<PgDn> - Go to the next tab on the left
- :tabdo command - Applies command to all opened tabs. (e.g. :tabe %s/old/new/g)
For more on tabs, type :help tab-page.
Using the Mouse
Shortly after learning about tabs and split windows, I found that I started spawning multiple tabs, each with multiple split windows in any given Vim session. I thought to myself, hey, wouldn't it be great if I can use my mouse cursor to resize the window partitions and click on tabs? Also, I secretly missed the option of using the scroll wheel and mouse clicks to browse code. It turns out, Vim lets you do all these things. Simply add the following to your .vimrc.
set mouse=a
Wonderful.
Paste Mode
If you have ever spent long minutes fixing indentation issues that occurred when you copied code like this:
void nestedMethod() {
for (int i = 0; i < 5; i++) {
//more nested code
}
}
And pasted it into Vim resulting in this:
void nestedMethod() {
for (int i = 0; i < 5; i++) {
//more nested code
}
}
This will be welcome news. Next time, :set paste before entering insert mode to paste indented text. Your status bar will show INSERT (paste) and your pasted text will retain its original indentation.
Of course, setting and disabling paste can get tiresome, which is why Vim allows you to map a key that will perform this function. Add the following to your .vimrc file:
set pastetoggle=<F2>
After you restart Vim (or :so $MYVIMRC), you will be able to use <F2> to toggle paste mode on and off.
Navigating Long Lines that Wrap
You run into a file with really long lines. Cursing the fool that created this file, you irritably :set wrap to see the entire line:
This is a really long line with wrapping text. This is a really long line that wraps. This is a really long line that wraps. This is a really long line that wraps. This is a really long line that wraps.
Next line.
Of course, when you hit j from the beginning of the file, it jumps to Next line. instead of the next display line that contains the wrapped text. You swear in mind-bending frustration. Does this sound familiar? Have no fear, gj is here! gk and gj are like k and j, except they move by display line rather than actual lines. Add the following lines to your .vimrc:
nnoremap k gk
nnoremap j gj
nnoremap gk k
nnoremap gj j
This will non-recursively map k to gk, vice versa, and similarly with j in normal mode only. What this means is that now k and j will move by display line. If you want to move by physical lines, use gk and gj instead.
Other Tips
- Text Objects
- Vim has a series of selection commands that start with "i", which stands for "inner". These are very useful when programming. For example, yiB will copy the inner block of code bounded by { and }. ci" will delete text within "", and put the cursor in insert mode. These commands work in visual mode as well; if you enter visual mode and type ib, it will select all characters within ( and ). For more on these, :help text-objects.
- Useful Motion Commands
- Next time you need to go to the top of a long method or a loop, try [{. This will bring you to the previous unmatched {. Vim has a whole slew of useful motions like [{, including motions for parenthesis and c-style comments. Read :help various-motions, and you won't be disappointed.
- Code Folding
- Like IDEs such as Eclipse, Vim can be made to fold code for easier browsing in large files. There is an excellent introduction to code folding at :help usr_28.txt.
- Browsing Code Intelligently
- Vim can be made to work with ctags or cscope. Ctags is a utility that will index all the "tags" in given source files and allow you to jump from the usage of the tag (which can be a method, variable, class, etc.) to its definition. Cscope is a little more elaborate to set up, but it is much more powerful. There are already detailed tutorials for setting up ctags/cscope, so I will forgo duplicating existing work. Quick Googling turns up this page for ctags and this page for cscope.
- Search Commands
- Vim has some special search commands besides / and :s that are quite handy. * is my personal favourite; used in normal mode, it will search for the word under the cursor, respecting word boundaries. That is, *ing Fred will not match Frederik. # is a similar search command that will search backwards instead of forwards. As you can imagine, they're great at searching for occurrences of variables or functions. Another favourite of mine is gd, which means go to local declaration. When used on a local variable, Vim will find and jump to its declaration. It's not perfect, but surprisingly accurate. Few another variations on these commands can be found at :help search-commands.
Conclusion
Wow, that was longer than I expected. And yes, I did get lazy at the end. I think the post is plenty long as it is. Hope it was useful!