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.cNow 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:
ENTER
- open the file. Will star the search and if only one match will be found the file will be opened.TAB
- hint. Will star looking for matching files but instead of opening those will show you the full path and you will be able to circle through all possible matches. UseSHIFT+TAB
to circle back. PressENTER
to open selected file. After going around all matches it will circle back to the entered search phrase.CTRL + D
- hint list. Similar asTAB
but will list out all the possibilities. You can use it to explore the contents of a directory.
All those create coherent system. The typical usage scenario looks like that:
:e * CTRL+D
- list directories at current path:e Proj*3/* CTRL+D
- list files and directories inside third project:e Proj*3/*model*/**/*order*.c CTRL+D
- list all files related toorder
in the third project's model directory:e Proj*3/*model*/**/*order*.c TAB, TAB, ENTER
- iterate over the results usingTAB
and pressingENTER
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:
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: