Using dmenu to open files quickly in Vim

I've been using the FuzzyFinder plugin for Vim for quite some time. It’s an invaluable part of my development toolbox. I've tried a handful other fuzzy style file finders, but FuzzyFinder always has been the most reliable.

FuzzyFinder in action

There’s one problem though, it’s slow. At work our codebase is tens of thousands of files and sadly FuzzyFinder chokes. Plagued with slow initial scanning and lagging your typing as it searches.

This is where dmenu comes into play. dmenu is a neat little program for Linux that provides a keyboard operated menu for selecting an item from a list. It’s really easy to operate, just pipe in the list of items and dmenu will pop up letting you pick one.

With a little bit of Vim script added to our .vimrc we can add dmenu support:

" Strip the newline from the end of a string
function! Chomp(str)
  return substitute(a:str, '\n$', '', '')
endfunction

" Find a file and pass it to cmd
function! DmenuOpen(cmd)
  let fname = Chomp(system("git ls-files | dmenu -i -l 20 -p " . a:cmd))
  if empty(fname)
    return
  endif
  execute a:cmd . " " . fname
endfunction

Here I've opted to use git ls-files to get the list of files to search from. All my projects use git, and this is a quick way to get the list of files without any build artifacts.

If you want to include every file from the current directory, git ls-files can easily be replaced with find ..

I call dmenu with a couple flags worth noting. The -i flag makes dmenu do case insensitive search, which I find useful. -l 20 is used to show the completion menu vertically (with 20 lines), which is much easier to read with long strings like file paths. Finally, I pass the command I want Vim to execute on the search result to the prompt of dmenu using -p.

I pass the command to the function so I can have two shortcuts for opening a file either in the current buffer or a new tab:

map <c-t> :call DmenuOpen("tabe")<cr>
map <c-f> :call DmenuOpen("e")<cr>

Ctrl-t will give me the file in a new tab, and Ctrl-f will give me the file in the current buffer.

dmenu in action

And that’s it! Your Vim file searching is now powered by dmenu. Ultra fast and ultra responsive.

dmenu’s string matching

dmenu’s matching isn’t exactly fuzzy, but it’s pretty good and it’s fast. The string you type is matched literally against each file starting anywhere, this means hello/world would match paths like hello/world, things/hello/world and files/hello/world.rb.

dmenu splits what you type by spaces, and uses each part as a pattern. The string hello world would match anything that contains both hello and world. This is great for narrowing down a too broad search.