Navigating project files with Vim

When I was learning to use Vim, finding my way around all the files in my
project was one of the major obstacles. But however tempting it may be to use a
plugin such as NERDTree to get a GUI-like file browser in your editor, The
Vim Way™ prescribes other, more powerful tools to get around.

This post describes navigating through a set of files using the :find and gf
commands. Vim also supports tag-based navigation and project-wide search
operations out of the box; and with plugins you can add fuzzy file finding and
more. But these basics go a long way in helping you navigate your project all
from Vim.

Finding project files by name

The simplest way to open a file in Vim is by using the :edit command, which
takes a path to a file. But even with tab-completion, typing out full paths is
cumbersome. Vim’s answer is the :find command, which allows us to recursively
search our project directory for a file by its filename. You can usually
autocomplete the filename you are looking for after entering a few characters.

The :find command uses the path option to determine where to look for
results. The path option is a list of directories or glob patterns Vim should
consider; it defaults to .,/usr/include,,. The . makes Vim consider all
files in the current directory, while the empty option (,,) tells vim to
consider paths relative to the current directory. You can inspect the current
value of path like so:

:set path?

In a Ruby project you would typically alter the path option to recursively
include the lib and spec (or test) directories:

:set path+=lib/**,spec/**

For Rails projects, you would want to include the app directory, but if you
are using Rails.vim, that is already taken care of for you. Rails.vim’s
:Emodel and :Scontroller commands are also implemented as wrappers around
:find: they basically map to :find app/models/ and :sfind
app/controllers/, respectively.

The :find command does not work like a fuzzy finder like CommandT or
CtrlP, but it handles most of the reasons why you would want to use such
plugins. See :help find for more information.

Jumping to file under cursor

When editing source code, you can use the gf command to jump to the file under
the cursor. That means that when you position your cursor on a path to a file in
your source code, pressing gf will open that path. That is moderately useful,
but it becomes better with the use of a couple of options that you can tweak:

when the current word is not an exact path to an existing file, Vim will look
for a file by that name in all locations in your path option (see :help
path);

when the file still can’t be found, Vim will try to suffix the word under
cursor with any of the suffixes listed in the suffixesadd option. For Ruby
files, you could add .rb to this list: :set suffixesadd+=.rb (see :help
suffixesadd);

finally, Vim will try to apply the expression in the includeexpr option to
the word under cursor to transform it into something more appropriate, such
as replacing . with / in Java source code (see
:help includeexpr).

There are a few tricks you can pull with gf-related commands:

Use gF to jump to a specific line number in the file. The line number should
come after the current word. For example, pressing gf on
lib/my_gem/foo.rb:30 would open up lib/my_gem/foo.rb, while gF would
open that same file and jump straight to line 30.

Use CTRL-W_f or CTRL-W_CTRL-F to open the file under cursor like gf, but
do so in a new split window.

When the file under your cursor does not yet exist, you can create a new file
with that name using :edit <cfile>.

Customising default settings

Most of these settings have sensible defaults in stock Vim distributions. For
example, the core Ruby plugin provides the following settings:

There is value in tweaking these. For example, we could adapt the includeexpr
option to allow Vim to directly open up the appropriate file when we press gf
on a class name by converting CamelCase words to snake_case:

Granted, this is a horrible one-liner with three substitution operations and a
terribly escaped regular expression — but it does let us position our cursor on
the word ApiClient and jump straight to lib/my_gem/api_client.rb with gf
which is pretty neat. Of course, jumping to class and method definitions is
typically a use case for tags (see :help tags) — but there is a slight
conceptual difference between jumping to a file with a certain name, and to the
definition of a class. Use it!

Moving back and forth

Of course, with all the jumping around between files it can be nice to navigate
back and forth through different locations much like we use the back and forward
buttons in a web browser. Vim has got us covered with the jumplist movement
commands. We move backwards and forwards through our history of locations in
individual and across multiple files using the CTRL-O and CTLR-I commands.
See :help jumplist for more information.

Refer to the documentation

Remember, Vim’s documentation is very good, descriptive and exhaustive. It
contains everything you need to know about these commands, options — and the
some. It’s easiest to read it straight from Vim through :help, but the
contents are also available online: