This chapter explores the many ways to open text files — or “edit text files” in Vim parlance — at startup and from Vim itself.
(more to come)
For argument’s sake
Like other text editors, Vim can be started without arguments:
in which case it creates an empty buffer for your convenience.
Or with one or more explicit filename as arguments :
$ vim file1 file2 file3 $ vim *.js
in which case n buffers are created and named after their respective argument.
The buffers are stored in the global buffer list and the filenames are stored in the global argument list.
The two lists differ in many ways:
the argument list only stores filenames whereas the buffer list also stores cursor position and state,
it is possible to insert a new “argument” anywhere in the argument list but it is only possible to add a new item at the end of the buffer list,
adding an item to the argument list also adds an item to the buffer list but the opposite is not true,
The buffer list is best seen as a partial representation of Vim’s internal state. Not being very flexible makes it reliable.
The argument list is best seen as a transient list of files. It is certainly possible to use it for navigation but its flexibility makes it less than reliable. Generally, the argument list is better suited for other tasks such as:
adding a bunch of files to the buffer list in one go,
holding a temporary list of files for later processing,
keeping a number of “special” files on hand…
:help argument-list :help buffer-list
Editing a single file
That’s all good but… how do we open other files from Vim itself?
:edit (shortened to
:e) is probably the most common way to edit a single file, whether that file exists on disk or not.
:e foo.txt :e ../bar.txt
Editing multiple files at once
:argadd (shortened to
:arga) can be used to add multiple files to the argument list:
:arga file4 file4 :arga *.js
:next (shortened to
:n) to edit the first one of those new files:
:arga *.c :n
or the short form:
If you don’t care about the argument list,
:arg can be used as a drop-in replacement for
:help :argadd :help :next :help :arg
:argadd cover a lot of ground already, but we will need fast fingers and a vast memory when we’ll need to edit files buried at the bottom of a large and complex directory structure.
Fortunately, tab-completion lets us cycle through eligible files:
Here are the default bindings:
<Tab>selects the next item,
<S-Tab>selects the previous item,
<Down>enters a directory,
<CR>chooses the selected item,
<C-d>lists the completion items.
But we are in the dark, here. We have no idea how large the completion list is unless we press
<C-d> every couple of keystrokes or if and how we could refine our query or how to leave the current directory! Basic tab-completion is better than nothing but meh…
Don’t worry! Vim has our back with a brilliant feature called “wildmenu” that temporarily replaces the status line with a handy menu:
Navigation is a lot easier now that we have an idea of where we are and what’s next. It is also easier to traverse directories with the new
<Up> binding. You can enable this feature for the current session with:
and for every further session by adding this line to your
vimrc, as usual:
Wildcards up our sleeve
Most of the time, though, the first two or three letters of a filename may not be enough… or we only remember the end. Or nothing at all beyond the extension! That’s where the “star” wildcard comes in handy, just like in your shell:
:e *fr<Tab> :e README_fr.txt
Vim even has a special wildcard called “starstar”, that makes it possible to recurse through subdirectories.
:e **/*use<Tab> :e app/controllers/user_management.js
“starstar” has two main benefits:
it allows us to navigate through a flat list instead of a potentially deep hierarchy, saving quite a bunch of keystrokes and brain cells in the process,
we can avoid typing subdirectories names.
What if we could skip the pretty but too repetitive
:e **/* part?
“Macros” are a core aspect of the Vim experience. The name is most often associated with “recording” but they can also be used directly, with
:normal, or as part of a mapping. The principle is always the same, though: we give Vim a bunch of keys to “press” very quickly, expecting the same result as if we pressed those keys ourself.
Mappings are very important when it comes to customizing Vim, simply because they turn repetitive actions into near-instantaneous magic. Allowing us to save many thousands of keystrokes with minimal configuration.
Here is the anatomy of a mapping:
:map key action
action is what you want to happen when you press
key; it could be a macro, an Ex command, a function call…
The command used to define your mapping —
map in the example above — can be any of the following, try to be as specific as possible:
normal, visual, select, operator-pending
Here are a few examples for you…
<F5>to add quotes around the word under the cursor in normal mode:
:nnoremap <F5> ciw"<C-r>""
<F6>to call a function in normal mode:
:nnoremap <F6> :call MyFunction()<CR>
<F7>to execute a command in normal mode:
:nnoremap <F7> :MyCommand<CR>
<F8>to filter the current visual selection through 'uniq':
:xnoremap <F8> !uniq<CR>
No need to worry about those examples for now.
Let’s go back to our current “problem”: we want Vim to type
:e **/* for us when we press
<F6> in normal mode. Well, the solution is pretty obvious:
:nmap <F6> :e **/*
Easy! We only have to do
<F6>foo<Tab> to list every file whose name contains
foo under the working directory and every subdirectory. Woohoo!
But what’s the deal with that
*noremap distinction? It’s really quite simple…
nmap key commandmeans that pressing
keyin normal mode will execute
commandwith its current meaning. This form is only useful when we want to use another mapping in our mapping; it is called “recursive mapping”. Example:
" change 'b' to work like 'B' :nmap b B " '<F5>' works like 'dB', not like 'db' :nmap <F5> db
nnoremap key commandmeans that pressing
keyin normal mode will execute
commandwith its default meaning. This form is usually the one we want, it is called “non-recursive mapping”. Example:
" change 'b' to work like 'B' :nmap b B " '<F5>' works like 'db' :nnoremap <F5> db
Our mappings have to be solid because they will serve as the foundation of our workflow. Non-recursive mappings are thus the safest choice:
:nnoremap <F6> :e **/*
A leader worth following
While the whole purpose of the
<Fx> keys is to be “programmed” to do whatever specific function the user needs, they don’t fit very well with Vim’s other highly mnemonic bindings so it is certainly wiser to use a key combo that “maps” to the idea of editing. But we have a problem: Vim already uses most — if not all — of the freaking keys on our keyboard!
The “leader” mechanism allows us to define a
<leader> key (
\ by default) that will work as a “mini-mode” of sort, or a “namespace” for our custom mappings.
:help mapleader gives us the following example :
:let mapleader = ","
which allows us to use the comma as
<leader> in all our mappings:
:nnoremap <leader>e :edit **/*
We are of course free to choose which key to use as our leader.
<Space>, for example, can be a more sensible choice because:
,is a very useful key (repeat last
fFtTin the other direction) with no alternative,
<Space>is synonymous with
<Right>so it can safely be remapped,
<Space>is the largest key of the keyboard and it can be pressed with any of our two thumbs.
Let’s end this section by adding these lines to our
let mapleader = "\<Space>" nnoremap <leader>e :edit **/*
and try them out after sourcing our
:help mapping :help mapleader
Vim comes with an often overlooked command fittingly named
:find that differs from
:edit in one big way: it can be set to visit specific directories.
The key to using
:find efficiently is to define a good value for the
path option that tells Vim where to find files. The default value may be a good starting point for C programmers but others can set it to a more generic — and simplistic — value:
which allows us to find files in the directory of the current file (the
.) and anywhere under the working directory, recursively, (the
) without needing to use
Or we can use a more project-specific value:
The sky is still the limit…
We can now use the
:find command as a slightly smarter replacement for
But there’s a catch: like
:find does its completion from the start of the filename so
:find foo will match
foobar.txt but not
model_foo.txt. Let’s add a wildcard for an even more useful completion:
:find in action:
:help :find :help 'path'
Customizing filename completion and the “wildmenu”
We can further customize the behavior of Vim’s filename completion with a bunch of options that work for
:find, as well as many other commands:
:help wildmode " defines the behavior of the wildmenu :help wildignore " tells Vim to ignore some patterns :help wildignorecase " enables case insensitivity :help suffixes " sets pattern-based priority
Let’s go through them one by one:
'wildmode' defines the behavior of the wildmenu. You can tell Vim to show a list of completions or not but also when to show it. It is recommended to play with the many possible combinations until you find the right one.
The default value,
full, is pretty good, here is anotehr reasonably useful one:
'wildignore' serves the same purpose as
.gitignore and similar configuration files: patterns are used to tell Vim what files/directories to ignore when doing completion. Again, the right values depend on your actual needs.
Here is an example value that ignores
+= operator that allows us to add new values instead of redefining the whole thing every time.
'wildignorecase' is a more generic variant of
'fileignorecase'; it allows this:
and is enabled with a simple:
'suffixes' is a mechanism that allows Vim to give low priority to files matching the defined patterns.
:help 'wildmode' :help 'wildignore' :help 'wildignorecase' :help 'suffixes'
But I need a file explorer!
Sometimes, we just need to find our way in the deep and uncharted waters of a project that was started by the guy whom just left the company. We only have a rough idea of the structure of the project and choosing what to edit on the command-line can be less than fun, even with our shiny mappings.
Thankfully, Vim comes with Netrw, a full-featured (some say “bloated”) text-based file explorer that allows us to dig down that new project much like we would do in a graphical file explorer:
Here are a few default bindings for reference:
<cr>open the file/directory under the cursor,
-go up one directory,
oopen the file/directory under the cursor in a new window,
Popen the file/directory under the cursor in the previous window,
topen the file/directory under the cursor in a new tab page.
And the three most basic commands:
:Exopen a listing of the current directory,
:Lexopen a listing of the current directory in a smaller vertical window, similar to the "project" pane common in other editors and IDEs,
:Rexcome back to the previous listing.
Netrw’s documentation is massive and covers a lot more than what you probably need for basic exploration and file-handling but you should at least take a look at the following sections…
:help netrw-browse-maps :help netrw-quickhelp :help :Lexplore
Opening files for editing is neither complex nor hard but — as with everything in Vim — it can be made quicker and easier with a couple of settings and mappings. Make sure you have exhausted the built-in ways before installing the latest and greatest fuzzy gadget people rave about on Reddit, Twitter or Hacker News.
But, now that we have a bunch of files to edit, how are we supposed to work with them?