Vim fuzzy finder vs starstar

Fuzzy finder - problems

I've been using Vim CtrlP plugin to fuzzy search and open files fast for many years. I've tried other solutions like fzf but CtrlP always worked out better thanks to the cache which gave it advantage on bigger projects.

With this plugin I could open files blazingly fast in comparison to my colleges using Visual Studio or other tools. But with time I encountered a few issues which one could summarise as: fuzzy finding is faster in smaller projects but the cost of that is the precision of search which is an issue in bigger codebases.

Problem 1 - other file paths contain searched file name

We try to find and open module_foo.c file. We do not remember the suffix at the end but that's just perfect for fuzzy searching. So we type just the module_ part but instead of our file we get a lot of unrelated ones: a.c, b.c, ..., z.c because their paths contain module word:

module/module_controller/module_special/a.c
module/module_controller/module_special/b.c
                                        ...  <and so on>
module/module_controller/module_special/z.c
some/other/path/with/file/module_foo.c       <-- finally

This problem can be fixed by switching CtrlP to match just on the file names instead of the entire paths but I never likes this solutions as it's both cumbersome and does not allow to specify the origin directory so in situation like this:

module/module_controller/module_special/a.c
module/module_controller/module_special/b.c
                                        ...  <and so on>
module/module_controller/module_special/z.c
AAA/other/path/with/file/module_foo.c
BBB/other/path/with/file/module_foo.c
.../other/path/with/file/module_foo.c        <and so on>
ZZZ/other/path/with/file/module_foo.c

you can't select the desired starting directory when CtrlP is matching only on file names.

Problem 2 - multiple files with the same name

Already mentioned in last example. The curse of the index.html files. The situation where only the localization differs can make usage of the fuzzy finder difficult.

a/b/b/b/module.c
b/a/b/b/module.c
b/b/a/b/module.c
b/b/b/a/module.c

Try fuzzy search that.

Problem 3 - many projects in the filesystem with similar structure and files

Similar to the previous one but here you have a lot of similar paths where the only difference is the first directory name. Fuzzy finder does not allow you to specify that your search string should start with some pattern. Examine the situation:

A/.../C/.../module.c
B/.../C/.../module.c
C/........./module.c
Now try to search for Cmodule.c. You will get your result but as the last one and now imagine that you have dozen of similar projects.

The fall of CtrlP

The problems mentioned happen on regular basis but I was managing. The advantages of CtrlP outweighed the occasional issues. Eventually I've got to work on a much bigger codebase - which changed my mental image of a big project. Even the CtrlP's cache was not enough and the time it took to build the cached was measured in minutes. Imagine waiting minutes for your fuzzy finder to build a cache after every commit pull or branch switch.

In such big codebases any tool I would have tried was slow. Only using git ls was a viable solution. The IDE with LSP was kind of helpful but again it took minutes to load.

And then I discovered Vim's starstar :e **/*

starstar aka :e **/*

Fuzzy finder has to always search through all the files - and that's why after reaching certain amount of those it becomes slow enough to not be useful anymore. Also the more files you have the more chance of encountering one of previously mentioned issues. The solution is to pass more specific search information and in Vim the starstar wildcard mechanism is what allows for that.

By putting two stars ** into path we tell Vim that there is unknown amount of unknown directories. For example by typing in:

  :e **/module.c

we tell Vim to search in all subdirectories for module.c file.

All Vim commands which deal with paths support this: :edit :split :vsplit :tabe and so on.

This is not faster then CtrlP and does not have cache in itself but when used in conjunction with other Vim mechanisms (as single star and buffers) you can achieve complete file searching system which is more precise and therefore faster than just fuzzy matching all the paths.

More accurate description of what we are looking for

We can put stars anywhere we want in the path to accurately describe what we want. Here are some examples:

:e Project1/**/module.c           ; Search recursively for module.c but only
                                    in the Project1.

:e Project1/**/Home/**/index.html ; Fix to the issue of having many files with
                                  ; the same name.  Here were are looking for
                                  ; index.html only inside Project1 Home directory.

:e Proj*2/**/Contr*/homecon*.c    ; Single star is a great help to type less.
                                  ; Here we would like a homecontroller.c file
                                  ; from the Project2.

:e */Views/**/Special/index.html  ; We don't always have to search recursively.
                                  ; If we know that there is only one level of
                                  ; directories to search into we can use a
                                  ; single star.  Here we are searching through
                                  ; all the projects to find which ones have
                                  ; Special index.html view.
                                  ; Its faster than :e **/Special/index.html
                                  ; because we provided more context.

:e **/MyService/**/MyCode.c       ; Here we don't know where we have our file
                                  ; for our service so just look through all
                                  ; projects - but the service name is know
                                  ; so it will greatly speed up the search.

The above examples showcase how combinations of two stars and a single star allows to precisely specify where to search for our file. In small projects it is not necessary at all - and that's where the fuzzy finder shines - but in huge codebases is irreplaceable.

Also it turns out that it is easier to stop the search. The CtrlP will just hang when there is too many files. But starstar search can be interrupted just by pressing CTRL+C and you will be back in the prompt to improve your search pattern.

But what to do if we do not know what to search for and we want to just explore the filesystem. That's where the CTRL+D comes in - with some TAB help.

The System: starstar + star + CTRL+D + TAB + ENTER

After we entered our search phrase e.g. :e **/module.c we can do a couple of things:

All those create coherent system. The typical usage scenario looks like that:

  1. :e * CTRL+D - list directories at current path
  2. :e Proj*3/* CTRL+D - list files and directories inside third project
  3. :e Proj*3/*model*/**/*order*.c CTRL+D - list all files related to order in the third project's model directory
  4. :e Proj*3/*model*/**/*order*.c TAB, TAB, ENTER - iterate over the results using TAB and pressing ENTER to open one of the selected files

The short video below is an live example. In this case the entire operating filesystem served as an example of a huge codebase:

A video recording with example of using starstar to explore filesystem

I use CTRL+D the most. To exit the pagination type q. You can scroll it up and down using standard j/k. I intentionally do all of that slow so you can fallow. The goal was to showcase how you can explore huge codebase from withing Vim using starstar.

Useful settings

By default starstar is case sensitive which defeats the whole purpose because typically we don't know what we are searching for. You can turn it off by setting:

  set wildignorecase

Additionally you can enable wildmenu which will show you the file paths as you will be cycling through them using TAB:

  set wildmenu

There are two more: wildignore and suffixes. First allows you to ignore specified patterns. The second one to specify priority. It seems obvious to put .git or node_modules in there but I don't do that. The ability to precisely specify the path makes it so that such directories are not as much a problem as in regular fuzzy finding.

Some hints

I have not described all the details related to starstar. Make sure to read the :help starstar in Vim. But here are a couple of notes from me.

There is a difference between :e src/**/file and :e src**/file on some operating systems. But it's not that problematic.

It's worth to get yourself familiar with CTRL+F shortcut in the command mode. It allows you to edit the command with Vim motion keys. You can then go back with CTRL+C. It greatly extends all we talked about because it will allow you to search through the history of commands and re-execute previously typed search patterns.

Summary

This short post does not describe all the nuances and possibilities within the Vim file search/open mechanisms. It all works without any plugins and works on all systems.

But as I said before - I don't think it's faster than fuzzy finder. It's more precise and allows for exploration which makes it more universal and better for big projects.

Personally I resigned from using fuzzy finding long ago and this is my favorite way to move around and open files. But the old habit of using CTRL+P shortcut stayed so I mapped it:

  noremap <c-p> <Esc>:echon getcwd() . "/\n"<CR>:e 
  noremap <c-l> <Esc>:echon getcwd() . "/\n"<CR>:b 

And here is the best part: all of that works with already opened files in buffer list. So once you opened your files you can use starstar to quickly open already loaded buffers - which is essentially cache. And that's what above CTRL+L shortcut is for.

One more useful mapping for you:

  " show current file directory content
  noremap <c-k> <Esc>:let @d = expand("%:h")<CR>:echo getcwd() . "\n"<CR>:e <c-r>d/*<c-d>

The last screen is from my vimrc in which I commented out CtrlP configuration just to test this new approach. It's been years since I did that and I never went back:

Vimrc configuration with commented out CtrlP settings
Vimrc configuration with commented out CtrlP settings