Vim’s file navigation is so powerful because it treats files and buffers as first-class citizens, not just something to be edited.
Let’s see it in action. Imagine you’re working on a Python project. You’ve got a main.py and a utils.py file open.
:ls
This command lists all the buffers you currently have open. You’ll see something like this:
1 %a "main.py" line 1
2 # "utils.py" line 1
The %a indicates the currently active buffer, and # indicates the alternate buffer (the one you last switched from).
To switch between these, you’d use:
:buffer 2
or more concisely:
:b 2
Now :ls will show 2 %a and 1 #. You can also switch by name:
:buffer utils.py
or
:b utils.py
The real magic happens when you need to find files you don’t have open yet. Vim’s built-in file explorer, :Explore (or :E), is a simple but effective way to browse your filesystem.
:Explore
This opens a new split window showing the directory structure of your current file. You can navigate this like any other Vim buffer: j and k to move up and down, Enter to open a file or directory. Typing h will go up one directory.
But what if you know the name of the file you want, or part of it? That’s where tab completion and fuzzy finding come in.
When you type :buffer and hit Tab, Vim will cycle through all files in the current directory. Keep hitting Tab until the filename you want appears.
For more advanced fuzzy finding, plugins like fzf.vim are indispensable. With fzf.vim installed, you can type:
:Files
This opens a powerful fuzzy finder. Start typing main.py and you’ll instantly see matches. Hit Enter to open the selected file.
The core concept is that Vim maintains an in-memory list of all files you’ve opened, called buffers. When you open a file, it becomes a buffer. When you save a file, Vim updates the buffer and the file on disk. You can have many buffers open simultaneously, and switch between them instantly.
The :args command is also fundamental. It manages a list of files that Vim will cycle through with :next (:n) and :previous (:N). You can populate this list manually:
:args *.py
This tells Vim to load all .py files in the current directory into the argument list. Then :n will take you to the next Python file, and :N to the previous.
Many people don’t realize that :args is often populated automatically by your shell when you launch Vim. If you type vim *.js in your terminal, Vim automatically runs :args *.js for you.
Beyond simple buffers and argument lists, Vim has a concept of windows (splits and tabs) and projects. Understanding how these interact with buffers is key to efficient navigation. A window is a viewport onto a buffer. You can have multiple windows displaying different buffers, or even the same buffer in different locations.
The :split command (:sp) and :vsplit (:vs) create new windows. :sp filename opens filename in a horizontal split. :vs filename opens it in a vertical split. You can then navigate between these windows using Ctrl+w followed by h, j, k, or l.
The :windo command executes a command in every window. For example, :windo set number will turn on line numbers in all open splits.
The next concept you’ll grapple with is managing larger projects, often involving multiple directories and complex dependencies, and how Vim’s built-in features and plugins handle that complexity.