This is a bit of an addendum to my original introduction to Vim and a glimpse into some of its more advanced features. This also serves as a reference for anyone interested in a lightning talk I gave at the July 2010 CDMUG. I tried to spend that entire presentation wowing the crowd rather than teaching them how the Vimpossible can be accomplished.

How do you move around so fast?

Movement commands are the key to working efficiently, so much so that most of the operations within Vim take movement parameters to know which text to operate over. First and foremost, use the h,j,k, and l (or arrow keys) to move a single space in each direction. The w key allows you to move forward a “word” and b moves backwards one word. You can even use ( and ) to move forwards/backwards a sentence and { and } to move by paragraph. These may be useful if you plan to write a lot, but developers will be more interested by %, which moves to the “matching” character/sequence. This would move to the matching closing brace, for example, or (with the matchit plugin) the matching HTML tag. * searches for the word under the cursor, moving to the next match. n moves forward to the next match for the last search term while N searches backwards.

Each of these movement commands can be tied with a number (x) to mean “move distance x times.” You may want to move three words forward, for example, or find the fourth instance of your search text. This becomes very useful for operations such as d (delete) or c (change), which accept movement parameters. This allows you to quickly delete the next sentence or change the preceding two words. Two of the most commonly needed parameters (to delete/change a whole line or to delete/change all of the line following the current position) can be performed with either cc/dd to affect the whole line or C/D to affect everything following the current position. $ or End will take you to the end of the line so that C is equivalent to c$; ^, 0, or Home will take you to the beginning.

You can also jump around your document via line numbers and other markers. Simply type 40G to jump to the 40ths line. gg will take you to the first line (as will 1G and 0G) and G will take you to the final line in the document. You can mark a particular line so that you return to it later by using the m key. Each mark has a single-character name such as “f” so that you can mark the line which contains a particular function via mf. Later, you can recall (move the cursor to) that function via ’f. Does that answer your question? :)

Can I open multiple files at once?

There are many different ways to edit or view multiple files with Vim. The simplest is to simply pass each file as a parameter when opening vim: $ vim file1.php file2.php or $ vim file*.php. You will now see the first file, but can switch to the next with :n or :next; switching back to the first is as simple as :prev. Combining this with the write command (i.e. saving), you can quickly modify a sequence of files via :wn commands.

You can also open two files in the same window by using :split, which will open the current file in a second viewport above the current. You can move between the two views of the same file by using the mouse (be sure to :set mouse=a in your .vimrc) or by pressing <C-w><C-w> (control + ‘w’ twice). :split somefile.txt will create a new viewport viewing somefile.txt and :new will split into an empty file. You can add a ‘v’ in-front of the commands (:vsplit and :vnew) to create a vertical split instead. You can even create splits within splits and then adjust the divisions with the mouse, creating a completely custom division of your work space. Though I have yet to find a good use for this, you can impress your friends by rotating the windows around by using <C-w>r.

One of the newest features to Vim is tabs, which allow you to have multiple groupings of windows open. This would be as if firefox allowed you to open multiple pages within a single window as well as opening multiple tabs. To create a new tab, use :tabnew, which optionally accepts a filename to open in the tab. You may then switch between tabs using the mouse, :tabn or gt to move to the next tab and :tabp or gT to move to the previous. Combine with terminal emulation, tabs really allow you to perform all of your coding and system operations from a single shell running vim.

What about syntax highlighting and indentation?

There are several commands that you can place in your .vimrc that will assist you as you develop software. The first, :syntax on tells vim to highlight the syntax of whatever filetype you are working with. If you are working with a file whose type is not properly recognized, you can force a particular syntax by using a command similar to :set syntax=php. Though there are lots of ways to add or alter the color schemes, I tend to find that the default syntax highlighting is just fine until I use a terminal with a dark background. Using :set bg=dark fixes this, and :set bg=light will return it. To add additional PHP-specific highlighting, you can add let php_sql_query=1 to highlight SQL within strings and let php_htmlInStrings=1 to highlight HTML within strings to your .vimrc. You’ll probably also find the :set number option to be friendly, as it adds line numbers to the current buffer.

Indentation is controlled by a few commands, including :set autoindent which will indent your cursor for function definitions, for loops, etc. and automatically outdent (that’s not a word) it when you reach the end of the block. :set expandtab makes the tab key insert a certain number of spaces rather than the tab character. This is a must if you ever plan to share your code as tabs will invariably appear at different lengths for different users. Two related options, :set sts=2 (or :set softtabstop=2) and :set sw=2 (or :set shiftwidth=2) declare how many spaces are in a tab/indentation. With both of these set to two, whenever I press the tab key or jump into a function, my cursor will be indented two spaces from where it was. The shiftwidth option is also used to determine how far to in-/outdent when using the >> or << commands.

My .module and .inc files don’t appear as PHP?

Vim uses a very simple file-extension=>filetype scheme to figure out how to highlight syntax, perform auto-completion, etc. Drupal (and some other PHP applications) prefer to include their PHP as .module and .inc files. This is easy enough to fix by adding and autocmd which will explicitly set the filetype whenever you load one of these files. Explicitly, you should add

autocmd BufRead,BufNewFile *.module set filetype=php
autocmd BufRead,BufNewFile *.install set filetype=php
autocmd BufRead,BufNewFile *.inc set filetype=php

to your .vimrc so that it associates .module, .install, and .inc files as PHP files.

How do I get and use code folding?

Vim has several configurations regarding code folding. If you are going to stick with PHP, the simplest folding mechanism is to add let php_folding=1, which folds class and function definitions such that

function example($param)
// some functionality here

will be turned into a single, expandable line function example($param). Getting a bit more sophisticated, Vim offers several options for a fold method (:set foldmethod=option). You can set this to indent to fold for lines with equal indentation, which would be optimal for a language like Python. You could set it to syntax which folds based on the file-type for languages that support this (I do not believe vim’s PHP support does). I use :set foldmethod=marker to indicate that I want folds to be based on a particular marker (sequence of characters). I then use :set foldmarker={,}, which tells vim to create folds between every set of matching curly braces (which handles for loops, closures, etc. as well as class/function definitions). Finally, I use :set foldlevel=1, which makes it so that whenever I open a new php file, the outermost fold is open. This is great if you use a lot of PHP classes, because you’ll want to see the class’ methods but not their implementation. If you are just using Drupal (which avoids class definitions), you will probably want to leave this to its default (0).

Navigating folds is a snap. Use zo or the right arrow key to the fold which the cursor is on. Using zO will open not only the fold, but recursively open all folds within it. zc closes the current fold (from anywhere within it) while zC recursively closes to the highest level. zR opens all folds within the file. To manually create a fold, simple use zf + a movement. For example, you could create a fold of the next five lines with zf5j or the next paragraph with zfap. Note that when using a marker, vim will actually insert this marker into you code (thought it will attempt to comment the marker if possible), so be careful.

How do I enable/use code completion?

When in insert (or replace) mode, you can activate “omnicompletion,” which will do it’s best to resolve the function, variable, etc. that you have started to type. For coders, you will generally use <C-x><C-o> (that is, hold control and press x, then hold control and press o). This is easy to remember, just think about Vim giving you hugs and kisses (which it doesn’t generally do. Omnicompletion will look through the dictionary of functions, variables, etc. and attempt to find the best for this situation, offering several as alternatives which can be selected via the arrow keys followed by Enter on the proper selection. When flipping through the various function endings, you’ll notice that a new scratch window opens, showing each’s signature. This window will stay open so that you may complete your call.

Before any of this can be useful, however, you must give Vim a proper dictionary of keywords, functions, etc. Vim provides a list of PHP-related terms which should be included when editing PHP files. To do this, include au FileType php set omnifunc=phpcomplete#CompletePHP in your .vimrc. This says that whenever the filetype gets set to php (usually when opening the file), include the standard library of PHP functions, keywords, etc. Omnicompletion will also pay attention to methods, variables, etc. found in the current file, and you can include your own library by include its tags (learn more). With tags, the current file, and the php library at your finger tips, you’ll find that you won’t need to Google “php string methods” or “drupal hooks” ever again.

And how do I jump to the function code definition?

Vim allows you to jump to the definitions of functions, classes, etc. so long as it is aware of their existence. Vim becomes aware of these definitions if they are coded in a standard format which is produced by the ctags (exuberant ctags) utility. Basically, this program will spit out a bunch of function, class, constant etc. definitions into a single file, which Vim can then load to figure out where to go when you want to learn more. For drupal/php work, I’ve written a script which will generate a tags file for the current code branch by running $ ctags --langmap=php:.php.module.inc --languages=php -R . which says, “include any file ending with .php, .module, or .inc as a php file,” “create tags for php only,” and “look at all of the files recursively within this directory.” Running this command (it will take a moment) generates a file named “tags,” which Vim will check for. Instead of requiring this file be in the current directory and named “tags,” Vim allows you to define the tag file via the :set tags=/path/to/file.

Once the tag files are loaded, you will have the included methods, constants, etc. available for omnicompletion, but you will also be able to jump the a function definition. To do this, move the cursor on top of the string in question and then run <C-w>]. This will cause the window to split, opening the file that defined the function (etc.) and jumping to the exact line of the definition. You may also type <C-]>, which will open the definition in the same viewport. This is particularly useful when debugging, as you can more or less follow the line of execution. You can then jump back (pop out of the definition) with the <C-t> command. Simple enough, right?

The Taglist plugin uses a lot of the same mechanisms (using ctags as its backend), but figures the file classes, functions, etc. specifically for the files you are actively editing or have edited during this session. To turn this on, run :TlistToggle, which will open a vertical split with the taglist viewer on the left side. As you edit files, they will be added to the browser in that window, allowing you to quickly see the classes, etc. that you’ve come across. This plugin has a very large number of options, notably TList_Process_File_Always, which tells the plugin to gather tag information even when it is not visible. To set this, add let Tlist_Process_File_Always = 1 to your .vimrc. As with many of the other plugins, you will probably want to add a hotkey to display this list by adding something like noremap <F6> :TlistToggle<CR>. The tag list has plenty of additional options (including the ability to recursively add directories and the ability to save/restore existing tag lists), so see the help documentation for more.

How can I check for syntax errors within my code?

Checking for syntax errors, or “linting,” prevents developers from wasting time testing a program/script that cannot work, and while clunky IDEs have included support for this for many languages, Vim has taken a more general approach. The basic idea is that you define the command line program which will lint over your source file using the makeprg option. For example, if using PHP, :set makeprg=php\ -l\ % will run the standard php -l command. This command will be ran whenever you run :make, which I strongly suggest mapping to some other key combination (e.g. noremap <F3> :make<CR> in your .vimrc). The output of this external program is then parsed for any errors using the errorformat option, which allows Vim to figure out which error, file, line number, column, etc. caused the error. If you set this up correctly, running :make will jump to the specific line which is causing a problem. For php -l. the error format should be something like :set errorformat=%m\ in\ %f\ on\ line\ %l. It’d be a good idea to set these based on the filetype with the au %FileType command in your .vimrc; au FileType php set makeprg=php\ -l\ %% would cover PHP.

How do I use external commands as input or filters?

There are three basic ways to run external programs from within Vim (four, if you count an embedded shell). The first is to run an external program and review the output. You may want to quickly see which subversion branch the current file is on, for example, so you run :!svn info. As you can see, the syntax is pretty straight forward, just type a colon, an exclamation point (“bang”) and then the name of the command as you would from the command line. As such, great way to quickly check what is eating up your server would be to run :!top. As we’ll see in just a moment, this form more or less drops you into a single, external command.

Now, what if you want to take the output of an external command and do something useful with it, i.e. use it’s output as input to your text editor? Simple add an ‘r’ before the bang and that program’s output will be placed into the current buffer. For example, if I’d like to find all source files that use a particular function, I can use :r!grep “;myfunc”; -R .; this becomes exceedingly useful when combined with the <C-w>f command, which will treat the string currently under the cursor as a file and try to open it. I use this all the time to grab a big list of files which contain a given method call that I’d like to refactor. I take that list of files, weed out any that don’t apply (svn copies, similar function names, etc.) and then ?<C-w>f each line to edit the corresponding function call.

You can also use an external program as a filter for the text within your current buffer. This means that you will send selected text to some program and Vim will replace the text with the program’s output. To do this, select the text in visual mode and type !command-name. Say you need to quickly modify a chunk of code to wrap at 80 characters; highlight the code, then !fmt and everything will be snazzy. What if you have a CSV file which you’d like to sort without importing it into Open Office? Open it in Vim, highlight all (ggVG) and then !sort. In both cases, you send the text in the filter to the external programs for processing.

What do I need to run a shell within vim?

There are several plugins that allow you to run a shell within vim, but I’ve found Conque Shell to be the best. You will need Vim compiled with Python (which is probably the case unless you compiled it yourself). The idea behind running a Conque Shell is that you split your window (or re-use an existing viewport) to run a particular shell, whether that be bash (an obvious choice), mysql/psql, an interpreter, or any other command-line program. To open an instance of top, simply type :ConqueTerm top. If you started to type that and then tried tab-completion, good call! To make it even faster, we can create a simple key binding. I use F5, so my .vimrc includes noremap <F5> :ConqueTermSplit bash<CR>. While in the shell, insert/append mode will type to the shell’s program. You can, however, press ESC to enter command mode and move around the shell’s output as if it were a text document. Cool stuff!

Is there some sort of file browser?

As with the embedded shell, there are multiple file browser plugins for Vim, but the most popular is NERD tree. This plugin will create a vertical split when the :NERDTree command is invoked, displaying a list of files and folders in the current directory. If you double-click (or press Enter) on one of these files, it will open in the most recently accesses viewport. Doing the same on a folder will expand that folder so that you may select its subdirectories or files. The interface is remarkably intuitive (especially for Vim) to the point where point-and-click will accomplish 99% of what you need. There is a lot of advanced functionality, including the ability to add bookmarks and a plethora of key-bindings (F toggles showing hidden files, A toggles a maximized window, etc.), but I will leave you to look at the help pages to discover them all. As with the embedded shell, you will probably want to tie this to a specific hotkey, adding something like noremap <F4> :NERDTree<CR> to your .vimrc.

© C.M. Lubinski 2008-2021