Speeding up Selecta in Vim

I like using Selecta as a fuzzy file opener in Vim. It doesn't do any caching, so I never have to remember to flush the cache after generating a migration in Rails.

The downside is that if you work on a larger project, it can get kinda pokey. Selecta's Vim functionality relies on find to populate the file list. If we tell find to ignore directories, it gets a lot faster. Here's how many files the default find command returns.

><((°> find . -type f | wc -l

That's a lot! Let's see if we can cut it down. I'm using a pretty typical Rails project as an example.

><((°> ls
Gemfile           README.md         coverage          spec  
Gemfile.lock      Rakefile          db                tmp  
Guardfile         app               lib               vendor  
Procfile          bin               log  
Procfile.dev      config            public  
Procfile.jobs.dev config.ru         script  

Logfiles are handy, but I never open them in Vim. If I need to see something, I'll tail it in the terminal. I also don't care about tmp. We'll exclude hidden directories too, just for good measure. Let's cut those out of the find command.

find . \( -path ./log -o -path ./tmp -o -path './.*' \) -prune -o -type f -print | wc -l  

Woah! We just excluded 10,865 files! That oughta speed things up a bit. But first I'll slow down and explain the command.

  • -o stands for OR. It breaks the find arguments into two major parts:
    • \( -path ./log -o -path ./tmp -o -path './.*' \) -prune
    • -type f -print
  • \( ... \) is a group. We're using it to OR several -path arguments together.
  • If one of the paths in the group matched, -prune kicks in and find ignores that path.
  • Otherwise, the file name is printed.

Now we just have to put it in our config. Here's what the old Vim mapping looked like:

nnoremap <leader>f :call SelectaCommand("find * -type f", "", ":e")<cr>  

Here's the new one:

let find_cmd = "find . \( -path ./log -o -path ./tmp -o -path './.*' \) "  
           \ . "-prune -o -type f -print"
nnoremap <leader>t :call SelectaCommand(find_cmd, "", ":e")<cr>  

This is also an example of how to set a variable and concatinate a string in VimL.

Now, this isn't totally optimal. I'd like to set up some directories that Selecta should ignore on a per-project basis. But it'll do for now.

Also, Vim isn't the only place that Selecta is useful! You can use it any place that you can pipe input into a command. This post has a bunch of other nifty examples.