How to search and open files in Vim without plugins

April 23, 2020

Follow @learnvim for more Vim tips and tricks!

You may find yourself in situation where you need to use Vim without plugins (inside a docker container or connecting to remote server for example). In that case, what will you do to search and open files quickly? You have to rely on Vim's built-in search methods. Luckily, Vim provides different ways to search and open files without plugins.

In this article, I will show different ways you can search and open files without plugins. Here are subjects I will cover:

I will also briefly list popular search plugins before concluding the article.

Opening files with :edit

:edit is the simplest way to open a file in Vim.

:e[dit] file
  • If file exists, it opens that file in current buffer.
  • If file doesn't exist, it creates a new file in current buffer.

Autocomplete (<tab>) works with :e. For example, if your file is ./app/controllers/users_controllers.rb, you can do:

:e a<tab>c<tab>u<tab>

edit accepts wildcards. If you are only looking for .yml files in root directory:

:e *.yml<tab>

Post tab, Vim will list you all .yml files in current directory to choose from.

You can also use ** to search recursively. Let's say you want to look for all *.png files in your project:

:e **/*.png<tab>

You may get warning that there are too many file names, depending on your project size.

:e integrates well with netrw, Vim's built-in text editor. If you pass it a directory instead of file, it will open a netrw explorer (which I will cover later):

:e .
:e test/unit/

By the way, when you opening a file with :e, you can also pass an Ex command.

:e +EX_COMMAND /your/file

To go to line 5:

:e +5 /test/unit/helper.spec.js

To go to the first line containing "const":

:e +/const /test/unit/helper.spec.js

To delete all empty lines:

:e +g/^$/d test/unit/helper.spec.js

Searching for files with :find

Vim has a :find method to find a file in our project. It finds a file in path, then edit it.

:find package.json
:find app/controllers/users_controller.rb

Autocomplete works with :find.

:find p<tab>              " to find package.json
:find a<tab>c<tab>u<tab>  " to find app/controllers/users_controller.rb

So far :find looks a lot like :edit. You may wonder, "Why should I use find at all? Can't I just use edit?"

The difference is that :find finds file in path. Edit doesn't.

Let's learn a little about path. Once we learn how to modify our paths, :find can become a powerful tool to search files.

To check what your paths are, do

:set path?

By default, yours probably look like this:

path=.,/usr/include,,

Here is what they mean:

  • . means current directory
  • ,, means anything relative to current directory
  • /usr/include is a directory.

Let's assume this is our project structure:

 app/
   assets/
   controllers/
      application_controller.rb
      comments_controller.rb
      users_controller.rb
  ...

You want to go to users_controller without going through each directory (and doing a lot of <tab> presses on the way). Path can shorten the journey.

Add controllers to your own path with:

:set path+=app/controllers/

What we just did is adding app/controllers/ to our current path (run :set path? again to see updated path). Now everything inside app/controllers/ path, Vim will look when we tab.

Before we added app/controllers/ to our path, if we type :find u<tab>, nothing happens. That's because Vim only sees everything inside current directory .. After adding new path, when we do:

:find u<tab> -> :find users_controller.rb

Vim finds and autocompletes it (assuming there is no other controller starting with u)

If you have nested directories inside app/controllers, you might want to add :set path+=app/controllers/** so autocomplete will find those files. The larger your directories, the slower finding each file may be.

If you're familiar with Rails, you might want to add paths for models (:set path+=app/models)and views (:set path+=app/views). You can also add the entire app directory into your path (:set path+=app/**).

You may be tempted to do something like:

:set path+=$PWD/**

To add everything in your project to your path so you can tab to any file quickly. While this may work for small project, I suggest you don't do it on large projects. On gargantuan projects, when I typed :find a<tab>, Vim tried to find everything starting with "a" and froze momentarily. I don't think this is how path is supposed to be used.

Adding paths for your frequently visited directories takes only a few seconds and it lets you find files quickly, saving you a lot of time. You can even make it permanent by adding it to your vimrc (ex: set path+=app/controllers).

Searching within files with :grep

If you need to find a phrase within files, you can use grep.

Vim has two different greps:

  • Internal (:vim[grep]. Yes, this command is spelled :vim)
  • External (:grep)

Let's go through Internal grep first. :vim has the following syntax:

:vim /pattern/ file
  • /pattern/ is a regex pattern of what we want to search
  • file is the file(s) we pass. It accepts * and ** wildcards.

For example, to look for all occurrences of "before_action" inside all ruby files within app/controllers directory:

:vim /before_action/ app/controllers/**/*.rb

You'll notice that once Vim is done, you don't immediately see all results. This is because the search result is inside quickfix (:h quickfix). To see the search result, type :copen.

I won't go over quickfix here. But here are some useful shortcuts to get started:

:cope[n]     " Open the quickfix window
:ccl[ose]    " Close the quickfix window
:cn[ext]     " Go to the next error
:cp[revious] " Go to the previous error
:col[der]    " Go to older error list
:cnew[er]    " Go to newer error list

You may notice that running internal grep (:vim) can get slow if there are many matches. This is because internal grep (:vim) reads all matches into memory. Vim loads each matching files as if they are being edited.

External grep, by default, uses shell grep.

To search for "require" inside a ruby file inside my controllers, I can do:

:grep require app/controllers/**/*.rb

Just like :vim, :grep accepts * and **. It also displays via quickfix.

Vim uses grepprg variable to determine which external program to run when running :grep, so you don't have to always use shell grep. I will not discuss how to do it here because the point is this article is to use default Vim config, but if you're interested, this article Faster Grepping in Vim shows you how to integrate with Ag and Using ripgrep With Vim shows you how to use it with ripgrep.

Browsing files with netrw

netrw is Vim's built-in file explorer. In order to get started, you need two minimum settings in your .vimrc:

set nocp
filetype plugin on

I will only cover the very basic of netrw, but it should be enough to get started.

You can start netrw when you launch Vim and passing it a directory instead of a file:

vim .
vim src/client/
vim app/controllers/

To start netrw from inside Vim while editing a file, you can use :e:

:e .
:e src/client/
:e app/controllers/

Alternatively, netrw has its own set of commands to launch the explorer. Some of them are:

:Ex      " Explore directory of current file (head)
:Sex     " Not kidding. It splits horizontally and start netrw on top screen
:Vex     " Split vertically

You can navigate with Vim motions. hjkl and searches like /?, work well. To go up one directory, use -.

While inside netrw explorer, sometimes you need to create, delete, rename file/directory.

%    " create new file
d    " create new directory
R    " rename file/directory
D    " delete file/directory

To move file/directory requires more steps, but you can check How do I copy a file in netrw? or Move a File with Netrw.

These should be enough to get you going. :h netrw is very comprehensive. If you have time, I highly suggest checking it out.

Some of you may ask, "Do I need to use netrw? Nerdtree looks much better."

It's up to you. I've used nerdtree for several years and I loved it. However, if you must use Vim without plugins and you're not used to netrw basic navigations, you may lose some speed.

By the way, vim-vinegar is a good (lightweight) plugin to enhance netrw if you decide to go with netrw.

Plugins

Last but not least, Vim has many plugins to let you quickly search for/ within files. Here are some of them:

Plugins are awesome. I personally use fzf.vim with ripgrep for searching. I would recommend to learn what Vim can do without plugins before you use one. Only use the plugins you need.

Conclusion

I think this is a good place to stop. I've shown you different ways to search and open files in Vim with:

  1. :e
  2. :find
  3. :vim and :grep
  4. netrw

You don't know when you'll have to use Vim without any. Be the Vim expert in your team.

Thanks for reading. Happy coding!

Resources