cody jh veal

Vim Killer Features Part 2: File Navigation

Written 2 months ago on July 12th, 2015

In the previous post of this series, I discussed text objects, which allow us to improve the efficiency with which we perform operations on text. In this post, I’ll be discussing how I quickly find the correct file to edit.

I use three methods of navigating files in Vim, depending on how well I know the location of the file I want to open. While some of these methods aren’t built in to Vim, nor are they impossible to replicate in other editors, I have found that these methods integrate into my Vim workflow exceptionally.

:edit command

When I know the location very well, or it is located near the top of my source tree, I will often use Vim’s command line to open the file. This method has the benefit of working just fine on a vanilla Vim install, but it can be greatly improved with a few tips and configuration tweaks.

First, the :e command opens files relative to the current working directory. You can check where Vim’s working directory is by running the :pwd command. You can optimize the number of keystrokes required to open files by changing your working directory with :cd. I usually keep the working directory at the root of the current project, and move down into a subfolder if my work is almost entirely contained within. See :help :cd for more information.

Out of the box, the command line in Vim offers tab completion, however, it can be vastly improved with the wildmenu and wildmode options. These options enable enhanced capabilities of tab completion on the Vim command line. I personally prefer using set wildmode=list:longest, which completes to the longest common prefix string among the possible choices and displays those partial matches as a list. There are many other options, and set wildmode=longest:full is also common, which behaves similarly but opens the wildmenu instead of displaying a list. Or you could combine the approaches by opening the wildmenu on the second tab with set wildmode=list:longest,full. Take a look at the full gamut of wildmode option values through :help wildmode.

Tree Exploration

When dealing with an unfamiliar codebase, I find it very helpful to be able to explore the directory hierarchy and get acquainted with the project structure. Another use case for tree exploration is when pair-programming or explaining a piece of code to someone—the speed of the other methods listed here can be a little disorienting for onlookers.

Vim has an explore view built in, called netrw and can be accessed by the :Explore command. When calling Vim on a directory, it also traditionally opens the explore view as well. Pressing i allows you to cycle through different views, one of which is a tree view. You can set this as the default by putting let g:netrw_liststyle=3 in your .vimrc file.

However, There are some issues with netrw, including producing lots of ‘junk’ buffers and even buffers that have modified and nomodifiable set. These can require you to close vim entirely to get rid of them. From what I gather, the tree list style is considerably more unstable.

There exist lots of plugins written to augment, replace, or facilitate the use of netrw‘s functionality. vim-vinegar is a plugin written by the prolific and esteemed Tim Pope. The inspiration for the plugin comes from an extremely insightful vimcast article which discusses how ‘project drawers’ don’t mix well with Vim’s system of splits (like oil and vinegar). vim-vinegar makes it simple to open an instance of netrw in the current buffer, and offers some handy shortcuts like opening a command line prepopulated with the file under the cursor.

I have used NERDtree for years as my explorer. However, splits are a huge part of my workflow and because of the points brought up in the oil vs. vinegar discussion above, I’ve found that this is the method I use the least in my day to day work. In researching this post I’ve come across Dhruva Sagar’s port of vim-vinegar which replicates the functionality of tpope’s vinegar using NERDtree instead of netrw. Having the explorer open in the same buffer as opposed to opening in the ‘project drawer’ style might make the explorer a more natural part of my general work-flow.

Since I do not use most of the heavy-duty features of NERDtree, I’m also planning on investigating alternate explorer implementations like vimfiler and vim-filebeagle to the heavier NERDtree. Another alternative may be to lazy-load the NERDtree plugin so that I only load the plugin when I’m attempting to open the explorer view, as it is infrequent that I open it during any given editing session.

Fuzzy Finding

This is the method I use most of the time when I want to edit another file. In general, it takes only a few keystrokes to uniquely describe a file, no matter how deeply nested it is. The fuzziness of the search allows me to type in as few characters as needed to distinguish the desired file.

I first installed command-t and loved it. After starting my dotfiles repo, I wanted minimal dependencies and replaced command-t with ctrl-p. I prefered the entirely vim-script implementation for portability.

I noticed a performance difference, mainly on large repositories. The time I spent waiting for the search to complete locked me out of searching, which often broke my flow. To improve the lookup times I implemented keybindings to narrow my searches to common directories, like /models or /lib. I would also change my working directory to limit the number of files searched through, using the same technique I described above. I found that this really made the tool more ergonomic, but in hindsight I see that I was simply working around the limitations of the implementation.

I have recently discovered a new fuzzy-finder named fzf that is magnificent. It was written in Go by one of my favourite vimscript developers, Junegunn. The choice of language gives us the ability to compile to a binary, meaning it maintains portability while acheiving a huge speedup on large searching. It isn’t neccessarily a Vim plugin, which is actually a good thing. I can use it from the terminal or I can invoke it through Vim. It comes with fuzzy autocompletion for bash and zsh.

Since fzf is not built specifically for vim, it’s designed to be very flexible and does not dictate or hold your hand much. You define a ‘source’ that gets a list of items, and a ‘sink’, a command which accepts an item of that list. For instance, you might define the source as all the javascript files in the current project, and the sink could be the :edit command. Or even more ambitiously, the source could return a list of synonyms for a word under the cursor and the sink could replace the word in the buffer with the selected word.

I found the fzf wiki to be a big help in getting my environment set up correctly, and it has plenty of examples of how to glue commands together into an fzf search. I’m currently using this AppleScript to integrate with iTerm2, but looking forward to getting tmux integrated into my workflow and completing the text triumvirate. For anyone looking to use fzf with Vim, I recomend checking out the fzf.vim plugin, also written by Junegunn. It defines a bunch of useful commands that you are free to call from the Vim command line or create keybindings of your choosing. Just a few examples are :History to edit most recently used buffers, :Tags to jump to project tags (see ctags), and :Colors to change colorschemes.