Introduction

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:

$ vim

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…​

Reference

: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

Reference

:help :edit

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

followed by :next (shortened to :n) to edit the first one of those new files:

:arga *.c
:n

or the short form:

:arga *.c|n

If you don’t care about the argument list, :arg can be used as a drop-in replacement for :argadd + :next:

:arg *.c

Reference

:help :argadd
:help :next
:help :arg

Tab-completion

:e, :arg, and :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:

gifcast off

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.

Pretty basic…​

Reference

:help cmdline-completion

The wildmenu

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:

gifcast off

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:

:set wildmenu

and for every further session by adding this line to your vimrc, as usual:

set wildmenu

Reference

:help 'wildmenu'

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.

gifcast off

Reference

:help starstar

Mappings

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

where 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:

recursive

non-recursive

mode

map

noremap

normal, visual, select, operator-pending

cmap

cnoremap

command-line

imap

inoremap

insert

nmap

nnoremap

normal

omap

onoremap

operator-pending

smap

snoremap

select

vmap

vnoremap

visual, select

xmap

xnoremap

visual

Here are a few examples for you…​

  • Press <F5> to add quotes around the word under the cursor in normal mode:

    :nnoremap <F5> ciw"<C-r>""
  • Press <F6> to call a function in normal mode:

    :nnoremap <F6> :call MyFunction()<CR>
  • Press <F7> to execute a command in normal mode:

    :nnoremap <F7> :MyCommand<CR>
  • Press <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 *map versus *noremap distinction? It’s really quite simple…​

  • nmap key command means that pressing key in normal mode will execute command with 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 command means that pressing key in normal mode will execute command with 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 fFtT in the other direction) with no alternative,

  • <Space> is synonymous with l and <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 vimrc:

let mapleader = "\<Space>"
nnoremap <leader>e :edit **/*

and try them out after sourcing our vimrc again:

gifcast off

Neat!

Reference

:help mapping
:help mapleader

:find

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:

:set path=.,**

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 explicitly.

Or we can use a more project-specific value:

:set path=app/views/**,app/controllers/**

The sky is still the limit…​

We can now use the :find command as a slightly smarter replacement for :edit:

:find foo<Tab>

versus:

:e **/foo<Tab>

But there’s a catch: like :edit, :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 *foo<Tab>

Here is :find in action:

gifcast off

Reference

:help :find
:help 'path'

More mappings

Again, we can go a bit further with a nice normal mode mapping similar to the one we devised earlier for :edit:

:nnoremap <leader>f :find *
gifcast off

Let’s replace the :edit mapping in our vimrc with the following:

set path=.,**
nnoremap <leader>f :find *

and move on to…​

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 :edit and :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

'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:

set wildmode=list:full

wildignore

'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 tags and cscope.out files:

set wildignore+=tags,cscope.out

Note the += operator that allows us to add new values instead of redefining the whole thing every time.

wildignorecase

'wildignorecase' is a more generic variant of 'fileignorecase'; it allows this:

:e read<Tab>

to yield:

:e README.md

and is enabled with a simple:

set wildignorecase

suffixes

'suffixes' is a mechanism that allows Vim to give low priority to files matching the defined patterns.

Example usage:

set suffixes+=.foo,.min.bar

Reference

: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:

gifcast off

Here are a few default bindings for reference:

  • <cr> open the file/directory under the cursor,

  • - go up one directory,

  • o open the file/directory under the cursor in a new window,

  • P open the file/directory under the cursor in the previous window,

  • t open the file/directory under the cursor in a new tab page.

And the three most basic commands:

  • :Ex open a listing of the current directory,

  • :Lex open a listing of the current directory in a smaller vertical window, similar to the "project" pane common in other editors and IDEs,

  • :Rex come back to the previous listing.

Reference

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

Conclusion

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?