Better Fuzzy Search with Ctrl-P in Vim
For those familiar with Sublime Text and have since moved to vim you probably miss the Cmd+P feature that allowed you to find a file in your working directory through a fuzzy match. The two main plugin contenders for fuzzy file finding in vim would arguably be Ctrl-P and Command-T. Personally I perfer Ctrl-P because it does not have a compiled extension and it offers a few niceties such as MRU and buffer switcher. Here’s a gif of it in action:
![CtrlP!]({{ site.url }}/images/ctrl-p.gif)
A common technique when using Ctrl-P is to set it up to use it in conjuction with the_silver_searcher as detailed in this blog by Thoughtbot. However, I noticed that often the order of the results are not quite up to par with Command-T or Sublime.
“Fixing” results from Ag and Ctrl-P
To get a better fuzzy finder with silver searcher and Ctrl-P the solution lies with another vim plugin called matcher. This plugin does require a compiled executable, which is kind of a drag in light of my above requirements. But, it is much easier to compile than the Command-T extension IMHO.
To get it set up with vim and silver searcher:
- Clone the repo and or install it with your vim plugin manager.
- Navigate into the repo folder and run
make
and thenmake install
- Open up your Vimrc and add the following
" The Silver Searcher
if executable('ag')
" Use ag over grep
set grepprg=ag\ --nogroup\ --nocolor
" Use ag in CtrlP for listing files. Lightning fast and respects .gitignore
let g:ctrlp_user_command = 'ag %s -l --nocolor -g ""'
endif
This should already be there if you are using silver searcher, but I’m including it for clarity.
Then add the following (slightly modified from the README on the matcher repo):
if executable('matcher')
let g:ctrlp_match_func = { 'match': 'GoodMatch' }
function! GoodMatch(items, str, limit, mmode, ispath, crfile, regex)
" Create a cache file if not yet exists
let cachefile = ctrlp#utils#cachedir().'/matcher.cache'
if !( filereadable(cachefile) && a:items == readfile(cachefile) )
call writefile(a:items, cachefile)
endif
if !filereadable(cachefile)
return []
endif
" a:mmode is currently ignored. In the future, we should probably do
" something about that. the matcher behaves like "full-line".
let cmd = 'matcher --limit '.a:limit.' --manifest '.cachefile.' '
if !( exists('g:ctrlp_dotfiles') && g:ctrlp_dotfiles )
let cmd = cmd.'--no-dotfiles '
endif
let cmd = cmd.a:str
return split(system(cmd), "\n")
endfunction
end
Close vim or re-source the .vimrc
and you should be good to go! Happy fuzzy
matching!