Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Vim without NERD tree or CtrlP

Vim without NERD tree or CtrlP

I used to use NERD tree for quite a while, then switched to CtrlP for something a little more lightweight. My setup now includes zero file browser or tree view, and instead uses native Vim fuzzy search and auto-directory switching.

Fuzzy Search

There is a super sweet feature in Vim whereby you can fuzzy find your files using **/*, e.g.:

:vs **/*<partial file name><Tab>

This will search the current directory and all directories below it for filenames containing your partial search. This can be used with :e, :sp, :vsp.

If there are multiple possible matches, you can see the options you have available by setting this in your .vimrc:

" Show file options above the command line
set wildmenu

For more information, run :h 'wildmenu'.

You’ll probably also want to exclude certain file types and directories from this fuzzy finding, so you can stick these in your .vimrc:

" Don't offer to open certain files/directories
set wildignore+=*.bmp,*.gif,*.ico,*.jpg,*.png,*.ico
set wildignore+=*.pdf,*.psd
set wildignore+=node_modules/*,bower_components/*

Now we’re at a point where I don’t need to look through any directories: if I know I want to open my _components.buttons.scss file I just need to do:

:vs **/*butto<Tab>

Vim will go off and find this for me, and if there are multiple possible matches it will also neatly display them all for me.

Auto-Change Directory

Vim runs from the context of wherever your ran $ vim from, so let’s say you have this simple project:

.
├── css
│   ├── components
│   │   ├── buttons.scss
│   │   ├── modal.scss
│   │   └── nav.scss
│   └── main.scss
├── index.html
└── js
    └── modules
        ├── buttons.js
        ├── modal.js
        └── nav.js

If we run $ vim index.html from here, we’d need to run the following within Vim in order to open the button’s Sass file in a new vertical split:

:vs css/components/buttons.scss

Them from there, to open the modal.scss, you’d need to run:

:vs css/components/modal.scss

This is kind of annoying, but we can get rid of if by telling Vim to track our current file’s directory! Drop this in your .vimrc:

" Set the working directory to wherever the open file lives
set autochdir

Now all of our file paths are relative to the current file, not absolute based on where we initialised Vim from! This means that we only need to do this in order to open our modal’s Sass file:

:vs modal.scss

…because Vim automatically moved us into the css/components/ directory.

You can toggle this by running:

:set autochdir!

…or you may prefer to make a mapping.

gf

Add this to your ~/.vimrc:

" `gf` opens file under cursor in a new vertical split
nnoremap gf :vertical wincmd f<CR>

When your cursor is over a path to a different file, hitting gf (go to file) will open it in a new split window. This helps you quickly explore the project based on its depedencies.

Without the above mapping, gf will still open the new file but in the same window.

Ack

If you’re not familiar with Ack, give it a quick read and then install Ack.vim. Now we can find things inside of files across our entire project by doing this:

:Ack --html 'button'

This will search all HTML files in the current directory (be mindful of your autochdir settings) and all directories below it for the string button. It will then populate your Quickfix list (:h quickfix) with all matching results (this is basically project-wide Find).

We can then take this one step further (if we need to) by running :cdo commands: a way of running a command over all of the results in our Quickfix list. Let’s say we found all instances of button by using the above, and now we want to change them all to btn. We can run this:

:cdo %s/btn/button/gc | update
  • :cdo tells Vim to do something across all items in our Quickfix list.
  • %s/button/btn/ tells Vim to look at every line (%) and substitite all instances of button for btn.
  • gc tells Vim to do this globally on each line and asks us, the developer, to manually confirm the change.
  • | update tells Vim to update the Quickfix list after making changes

All Together

So now we’re in a position where we can fuzzy search for file names, we can move Vim into our current directory to make things simpler to look for, we can do global finds using Ack, and global find and replace using the Quickfix list.

No more need for file explorer plugins!

crivotz commented Mar 29, 2017

Thanks for tips. I will give it a try

I use fzf for pretty much everything. I keep NERD around for when I feel like aimless browsing or admiring the pile.

Nice post, especially cdo is a very helpful trick.
While I appreciate a minimalistic setup (I prefer to learn native shell scripting/vim scripting over a whole bunch of dependencies), I like CtrlP simply because it's so much faster to input than typing out **/*something-something, especially because you can just chuck together parts of directories. And also I think it searches a lot quicker than Vim's native completion.

pirey commented Mar 30, 2017

Hi,

Nice tips on how vim could do fuzzy search by itself. And I like the idea of minimalistic setup.

For the autochdir however, what if you want to open multiple files at the same time?
Say after you open :vsp css/components/buttons.scss, you want to open nav.js, then you end up typing ../../js/modules/nav.js since fuzzy search is for current directory and all directories below like you said. What do you think?

The other problem is, how do we know where that file live? I think NERDTree (or at leat netrw ) is useful for this. The other advantage of directory browser is, in my opinion, it let us inspect the shape of our project structure, interactively.

plcplc commented Mar 30, 2017

I settled on a different solution than autochdir to open adjacently placed files:

(in the context of the file foo/1/2/3/bar.txt, wanting to open foo/1/2/3/baz.txt)
:e %:h/baz.txt

% expands to the current file
:h modifies the expansion to incluce only the directory.

Doing %:h will expand inline

Obscure? Arcane? Certainly! But so is all of vim :-D

demelev commented Mar 30, 2017

Good post, thanks for that!
I think it should be called "Opening files in Vim without NERD tree or CtrlP"
Because the topic suggests that NERDTree or CtrlP is superfluous, but NERDTree and CtrlP are not only about opening files.
Also these plugins use caching what makes its work very fast and speeds up your workflow.

ashwin commented Mar 30, 2017

These are some fantastic tips! 👍

Could you explain in your article what this actually does in Vim: **/*<partial file name><Tab>

williamvds commented Mar 30, 2017 edited

If you want to save yourself a few characters when fuzzy-searching for files, you can add ** to the path in your vimrc:

set path+=**

Then you'll be able to find files in subdirectories using just

:vap *startofname<Tab>

Is there a way to open index.html with the fuzzy-search if you are currently editing modal.scss ?

mike3run commented Mar 31, 2017 edited

Guys give this a try using the Leader key we can mimic ctrlp like a boss

nnoremap <Leader>p :e **/*
nnoremap <Leader>v :vsplit **/*
nnoremap <Leader>s :split **/*

In my case Leader is , so all together:

,p index <TAB><Enter>

I added the other 2 ones to split vertically or horizontally easily.

Yggdroot commented Apr 1, 2017

Good tips for beginners.
For a heavy vim user, using native Vim fuzzy search always need to type too many characters every time when searching a file in a large project.
Now I use LeaderF, real fuzzy search, only need to type a little characters.

rlue commented Apr 13, 2017

Re: :set autochdir

There are times when it's useful to keep vim locked to your project root – for instance, as you mentioned, when using :Ack:

This will search all HTML files in the current directory (be mindful of your autochdir settings)

Instead, I set the command-line abbreviation %% to equal the directory of the current file:

cabbr <expr> %% fnameescape(expand('%:p:h'))

(which is easy to remember, because % is the current file). So, if I want to edit modal.css, it's just:

:vsp %%/modal.scss

This way, I can always expect the working directory to be the project root.

:cdo is super powerful! Thanks for that. :)

aliev commented Jul 17, 2017 edited

Ag is more faster than Ack. So you can also use it even without any plugins. My Configuration looks like:

if isdirectory('.git') && executable('git')
  set grepprg=git\ grep\ -nI
endif

if executable('ag') && !isdirectory('.git')
  " Silver searcher instead of grep
  set grepprg=ag\ --vimgrep
  set grepformat=%f:%l:%c%m
endif

:grep something

(and it will super faster in "git project" because of git grep).

My key map to find word under cursor:

map <LocalLeader>g :silent grep! <C-R>=expand("<cword>")<CR>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment