Vim Tip #24: Vim 'path' and 'find'

2020-01-22(Wed)

tags: Vim

Vim Tips

It's been a long time since I've done a Vim Tip: I use NeoVim several hours every day, but I haven't had reason recently to look for new ways of doing things. But a coworker sent me a video he thought I'd appreciate: https://youtu.be/XA2WjJbmmoM ("How to Do 90% of What Plugins Do (With Just Vim)," 74 minutes). You may be better served by watching this video (although watching it would take longer than reading this). I haven't even finished watching as I write: I was so thrilled with the discovery of another wonderful bit of Vim functionality that I decided to write this first.

As the speaker points out, Vim's help system is ... perhaps annoying, but it's also one of the best sets of documentation built into any piece of software. So when I say that this blog entry is about the "path" command, you should immediately go to Vim and type :help path. This isn't difficult: what's difficult, and what I'm hoping to partially solve by doing a series of Vim Tips, is the process of discovery. Vim has thousands of commands, and finding out the ones that are useful to you now ... that's hard. I would also encourage you to use the cross references in the help system as a voyage of discovery: I was already going to tell you about the :find ... command, but because I just typed :help path I'm also going to tell you about the related :sfind ... command.

What the speaker suggests is that you put this line in your ~/.vimrc:

set path+=**

As per the reading you may have done (:help path), this affects several Vim commands: :find, :sfind, :tabfind, and gf plus associated f commands (I use gf but haven't used any of its friends and won't be covering them here). And what it does is really interesting: it adds all subfolders of the current start folder to the search path for all these commands. Consider the output of tree far an Ansible project:

├── ansible.cfg
├── group_vars
│   └── all
├── hosts
├── provision-fedora.yml
├── provision-webserver.yml
└── roles
    ├── nginx
    │   ├── files
    │   │   └── robots.txt
    │   ├── handlers
    │   │   └── main.yml
    │   ├── tasks
    │   │   ├── certbotcert.yml
    │   │   ├── main.yml
    │   │   └── selfcert.yml
    │   ├── templates
    │   │   ├── certbotmin.j2
    │   │   └── web0.j2
    │   └── vars
    │       └── main.yml
    └── common
        ├── handlers
        │   └── main.yml
        ├── tasks
        │   ├── configs.yml
        │   └── main.yml
        └── templates
            └── bashrc.j2

Typically, I'd open the top level provision-webserver.yml file. From there I can start a Vim command :find selfc<tab> and we get tab completion on selfcert.yml which actually opens roles/nginx/tasks/selfcert.yml. If you prefer to open the second file in a new tab (rather than a buffer), then use :tabfind selfcer<tab>, or if you want it in a split :sfind sel<tab>. If there's more than one match, Vim will revert to offering a choice from the matches. That makes main.yml a poor choice as I have a lot more roles than I show above. In that case, you should start the match with a role name (which admittedly reduces the usefulness of this trick, but I think it actually works better in other languages).

There's a caveat here: matching like this on a very large directory (like my ${HOME}) is likely to be either very slow or entirely non-functional. I haven't tested much, but I'm leaning to the latter. But it's a wonderful addition for working on programming projects.