Wednesday, May 28, 2014

For Argument Sake

The buffer list is dead! Long live the buffer list!


The Buffer List

I have long been a fan of :ls and the accompanying :b partial match on buffer name. That basic functionality is rock solid. I still recommend it to newcomers to our beloved editor. We’ve built plugins around it.

There is only one problem with the buffer list. Well, okay, two problems:
  1. It isn’t able to be reordered,
  2. It isn’t able to be renumbered.


Ordering

I sometimes wish I could re-order the buffers so that I could group related ones together: all my text files together and all my source files before them, say. The buffer number is fixed at the time the buffer is created and can never be changed throughout the lifetime of the Vim session - it’d be much nicer to be able to re-order these as and when you saw fit. You can’t do this with the buffer list.


Numbering

When buffers are removed from the buffer list, they leave holes in the numbering of listed buffers. Many plugins use temporary buffers (some a LOT) which can leave huge holes in the numbering of your buffers. If you like to jump to buffers by remembering their buffer number, it can be a bit unsettling to know that you need to jump to buffers 1, 2, 6 and 11 - it’d be much nicer to (even if only temporarily) renumber those buffers to: 1, 2, 3, and 4 respectively. You can’t do this with the buffer list.

But not all is lost. We have an alternative in Vim. We have the argument list!


Argument List

Read more about this little gem with :help arglist
Each window can have a separate argument list. You’re free to set and reset the argument list as and when you see fit. Let’s take a short walk down argument lane:

You can slurp up the .c files in the src/ directory into your current window’s argument list:

:args src/*.c

You can do that recursively within all of the subdirectories:

: args src/**/*.c

You can see your current argument list with:

:args

You can add the .h files to the argument list with:

:argadd src/**/*.h

You can jump to an argument by partial buffer (file) name match with:

:argedit {partial name}

You can jump to an argument by (1-based) index with:

:argument {index}

You can perform an operation on all of your arguments in a single command:

:argdo %s/Long live \zsthe buffer list\ze!/argument lists/ge | update

Early Quitters:

If you :quit before visiting all of the files in your argument list, Vim will question your intentions. To prevent that, use :qa instead.

Local vs Global:

If you spawn a new window, it will inherit the parent window’s argument list. There are functions which will attach to the global argument list or create a new local argument list for the current window.

:help argglobal
:help arglocal

Awesome, right?

Could it get any better? Do I ever ask that without candy in my pocket? Make an appointment with your dentist because this is so sweet, it’ll rot your teeth:


VimFindsMe

The light-weight file finder, VimFindsMe (VFM) now supports the argument list. By default, the <leader>ga map will open the current argument list into a scratch buffer. You can add and remove files and reorganise them as you see fit. You can see their positional index by enabling the :setlocal number option. When you press <enter>+ in this scratch buffer, VFM will set your argument list to these files, in this order.


From :VFMEdit

The :VFMEdit command (mapped to <leader>ge by default) lets you filter a find result on your :help path option. If you press <enter> on a file from this window, it will be opened as a new buffer (but not added to your argument list). If you’d like to set your argument list to the files in the VFM scratch window, type:

:VFMArgs

If instead you’d like to add all of the files to your argument list, type within the VFM scratch window:

:VFMArgadd

The cumbersome combination of :argument and :argedit have been combined into one convenient function called VFMArgument(arg) where arg can be either an argument index or partial buffer name. Remember that the argument index is not the buffer number. You can see the list of arguments with either the :VFMArgs command or the builtin :args command. The buffer name given must exist in the current argument list. This function has a corresponding command:

:VFMArgument {arg}

Which is triggered by <plug>vfm_argument (mapped to <leader>gg by default). The command supports argument list buffer name completion.


You Can’t Do This With The Buffer List

The list of :buffers (aka :ls aka :files) is still useful, don’t get me wrong. It is an unwavering record of buffer numbers and names, unassailable throughout the lifetime of your Vim session. Ignoring :bdelete and :bwipe for a minute. But apart from that, completely inviolable. Excepting certain &buftype settings that make buffers unlisted, of course. Besides all that… watertight.

The argument list on the other hand is an ephemeral construct, changeable on a whim and created with ease. It allows you to arbitrarily order and reorder your buffers as often as you need. And you can perform a string of commands against each buffer in your argument list with ease.

The argument list has a few warts, though, like having two commands for switching to an element by index or by name, and a cumbersome method of deleting arguments. VimFindsMe solves these problems by providing a better interface for setting, modifying and deleting the argument list (:VFMArgs), and a single integrated command (:VFMArgument) for jumping to an argument by index or name.

Care to argue?