Power to Productivity

Vim is a powerful tool which helps you stay productive and spend less time doing the mechanical work of coding. For many, Vim seems archaic, under-featured, or even intimidating. I hope to illustrate that Vim none of those things, but is in fact a great editor for nearly every development environment.

I want to focus on three common tasks where Vim helps you be more productive without leaving your editor. These tasks are: navigation between files, and searching / replacing. No Vim experience is necessary for this post, but it definitely helps if you are at least familiar with the basics through vimtutor.

One of the most common and time consuming activities we do as developers every day is jump between files. If you are using a more traditional text editor such as VSCode, Sublime Text, or Atom, chances are you spend more time that you would like scanning through your directory tree trying to find the right file. With Vim, the secret to file navigation begins with set path.

The path is where Vim searches for files when executing the various search commands. By default on Unix-like systems, it is .,/usr/include,,. Let’s break this down a bit. The first . indicates that Vim should include files relative to the current file’s directory. Directories that should be searched are separated by commas, so the next place Vim will look by default is the /usr/include/ directory. This directory typically contains headers so it can be useful if you are doing C and C++ programming. The final sequence of ,, instructs Vim to search in the current directory. Out of the box, we are able to search a large part of our project. What if we wanted to search downward recursively through our project? It is common to open your text editor in your project root ( often denoted by a vcs file such as a .git folder ). To ensure that Vim finds all of our project files when we search for them, we use * and **. The asterisks represent wildcards, with * matching 0 or more characters and ** matching only directories. By setting your path to set path=.,,,**, you can ensure that Vim will search all our project files. One caveat to this is that by default ** only searches 30 directories deep, so if you have an extremely nested directory structure, you can increase that limit, see :h starstar for more details. Now that we have our path correctly set, it’s time to navigate! Vim has a built in keybinding for jumping to the file under your cursor, and it is gf. We tell Vim which file endings it should add by using set suffixesadd. So for frontend web development, you might have something in your vimrc that looks like this: au! BufNewFile,BufRead *.js,*.jsx suffixesadd+=.js,.jsx. This allows us to jump to files with endings .js and .jsx.

In practice it looks like this:

import { someFunction } from "./utils";

Placing your cursor in normal mode on ./utils and pressing gf should take you to the utils.js file!

This is just the tip of the iceberg of what Vim can do when it comes to searching through your code, for a more in-depth take on this, I would recommend the blog post, death by a thousand files. I want to show one more useful command that benefits from having set our path to search downward recursively and that is find. With find, Vim will search for a file and then open it for editing if the file is found. We can also use * wildcard expansion and tab completion if we have the wildmenu setting on.

For example, typing: :find N*C*.jsx and pressing tab would expand to NavigationContainer.jsx. You could also type Navigation and tab to expand all the options that Vim finds in the current path. Just like that, you can fuzzy search through your project without any additional configuration or plugins. Sometimes, there are directories in the project which we would like to ignore, such as node_modules, target, and dist. Vim determines which files to ignore via the wildignore setting. A sample wildignore may look like this: set wildignore=*/dist*/*,*/target/*,*/builds/*,*/node_modules/*. Vim will no longer include these directories in it’s path.

Find and Replace

By default, Vim ships with vimgrep which allows you to search for strings within files. Vim also comes with the grep command which allows you to search using an external grep tool. I personally use ripgrep, a grep tool that is focused on speed and respects my .gitignore file by default.

To enable Vim to use ripgrep ( or any other grep program ) as a backend to the grep command, you need to set the grep program in your vimrc:

set grepprg=rg --smart-case --vimgrep

Now you have your search results loaded in the quickfix menu, and can leverage a Vim built-in plugin, cfilter. To add it, simply run :packadd cfilter and Vim will load the plugin for use. cfilter filters through entries in the quickfix list via regex.

If you have search results that look like this:

datastructures.html|623| 30:        mixpanel.track("data structures loaded");
            index.html|61| 26:          <a href="./datastructures.html"
            index.html|62| 19:            >Data Structure Memes For Edgy JavaScript Teens</a
            _site/index.js|64| 28:           import { customStructure } from './datstructures'
            _site/utils.js|64| 28:           import { customStructure } from 'custom/datstructures'

If you want to only keep the search results from the JavaScript files, you run this command: :Cfilter! html. This keeps everything except matches that contain html in the quickfix list. If you wanted only to keep only the third match, you run :Cfilter Memes and it keeps only matches containing Memes.

One more useful command when using the quickfix list is :cfdo. It allows you to execute whatever command you give it across the entire quickfix list. For example, if you wanted to search and replace you could do it like this:

search for the phrase, ‘data structures’:

:grep! 'data structures'

view results:

datastructures.html|279| 15:              Data Structures
  datastructures.html|574| 26:              with these data structures, since the structure of our data is
  datastructures.html|575| 43:              irrelevant to react, custom data structures should be simple to
  datastructures.html|623| 25:        mixpanel.track("data structures loaded");

change ‘structures’ to singular ‘structure’ and save every file:

:cfdo s/ures/ure | update

The | update part of that last command tells Vim to save the changes after it executes the replace. If you wanted to check your changes before saving, you leave that part off. You still have to save every file manually if you choose this option.

Grep tips

Vim makes finding and replacing throughout a large codebase fast and easy. To make it even faster and easier, here are some things for your vimrc.

set grepprg=rg\ --smart-case\ --vimgrep " set grep program to ripgrep with smartcase flag. You can set it to anything you like.

  " creates a :SearchProject command, makes it so you don't have to escape strings in ripgrep, will tab compete with directories in path.
  command! -nargs=+ -complete=dir -bar SearchProject execute 'silent! grep!'.<q-args>.' | cwindow'

I hope that this post helps improve your Vim experience!