Vim
Understanding the Buffer List in Vim: Part 1
Perhaps the most important component in editing text with Vim is the buffer. Understanding buffers, how Vim organizes them, and how you can manage them is essential to effectively using Vim.
A buffer is the in-memory text of a file.
Any time we open an existing file or create a new one using Vim, a buffer will be allocated as the in-memory representation of said file. Any changes we make will be tracked within the buffer. When we are ready to update the file, the buffer can be written to disk.
Vim allows us to work with a bunch of different buffers at once. When working on large projects, the list of buffers can quickly grow out of control. Being able to read, understand, navigate, and manage the list of buffers can be challenging. With the right tools at our fingertips, we should be able to get by. At the end of this series, we will know how to leverage many built-in Vim features and a handful of plugins to this end.
In Part 1, we will explore the basics of the buffer list. In particular, we will walk through a step by step scenario to see how Vim populates the buffer list and to learn what each part of the buffer list means.
Let's get started.
Listing Buffers
During a long running Vim session, we will quickly accumulate more than a few buffers. We can use ls
at any time to get a handle on the current list of buffers.
To see this in action, let's navigate to a new directory, touch a new file, and then open it up in Vim.
$ mkdir understanding-buffers
$ cd understanding-buffers
$ touch file.md
$ vim file.md
We now have a Vim session running with what we can expect to be a single buffer. Let's see.
:ls
1 %a "file.md" line 1
At this point, the ls
command reveals that we only have the one buffer open -- file.md
.
There is some other information displayed. The 1
is the buffer number. This is the first and only buffer, so it was assigned 1
. Other buffers opened during this session will be assigned numbers counting up from there.
The %a
is two pieces of information. The %
meaning that this line (buffer 1
) corresponds to the buffer in the current window. Vim uses windows as viewports for displaying buffers. In this example, there is only one window displaying a single buffer, so this must be the buffer in the current window. The a
has a similar meaning. It means that this is an active buffer that is loaded and visible. In a bit, we will add more buffers and see these indicators change.
Lastly, the line 1
part means that the cursor is currently on line 1
of that buffer.
The ls
command isn't the only way to list the current buffers. We are also free to use files
and buffers
which are synonymous commands.
Another Buffer
We can make things more interesting by creating a new file. We can do that with the edit
command.
:edit data.csv
Let's execute ls
again to see what has happened to our buffer list.
:ls
1 #h "file.md" line 1
2 %a "data.csv" line 1
The buffer list now contains two items.
The first item in the list, buffer 1
, now has different indicators: #
and h
. The h
means that it is now a hidden buffer[1]. Though the buffer is still loaded, it is no longer being displayed. Don't worry though, we can always switch back to it. The #
indicator means that buffer 1
is the alternate buffer[2]. For now, we can think of the alternate buffer as the previous buffer, but we will see later that this analogy isn't quite right.
The second item in the list, buffer 2
, is for the new data.csv
file. It has the %
and a
indicators which we recognize from the previous example. This makes sense because it is the current, active buffer.
More Buffers
Let's use the edit
command to create a couple more buffers. First, how about a ruby file
:e code.rb
and then a SQL file
:e schema.sql
At this point, we might want to take another look at our buffer list.
:ls
1 h "file.md" line 1
2 h "data.csv" line 1
3 #h "code.rb" line 1
4 %a "schema.sql" line 1
Buffers 1
through 3
are all hidden (notice the h
indicator) because none of them are currently in view. The buffer for code.rb
is the previously accessed buffer, so it gets the alternate file indicator (#
). Buffer 4
, as expected, gets %
and a
because it is the current, active buffer.
It seems like we've exhausted the set of indicators that can come up by just editing new buffers. So, what else can we do? Perhaps we can modify one of the buffers.
Modifying A Buffer
Let's make some changes to the buffer for schema.sql
and see what the buffer list has to say. We can enter Insert mode by hitting i
and then type something like the following:
select 1;
Now, hit <esc>
to return to Normal mode and then invoke the ls
command.
:ls
1 h "file.md" line 1
2 h "data.csv" line 1
3 #h "code.rb" line 1
4 %a + "schema.sql" line 1
We've dirtied buffer 4
by making an unsaved change to it which is indicated by a +
. As soon as we write the buffer, this +
indicator will go away.
:w
:ls
1 h "file.md" line 1
2 h "data.csv" line 1
3 #h "code.rb" line 1
4 %a "schema.sql" line 0
New Files
We've created buffers for a couple named files, but what about if we use the new
command.
:new
:ls
1 h "file.md" line 1
2 h "data.csv" line 1
3 h "code.rb" line 1
4 #a "schema.sql" line 0
5 %a "[No Name]" line 1
Vim doesn't have a name for the file represented by buffer 5
, so it just uses [No Name]
.
The other interesting thing here is that Vim opens a split window with the new
command. If we look closely at that buffer list, we see that there are two buffers with the a
indicator. This is because both 4
and 5
are loaded and visible. All the others remain hidden.
We can then write the contents of the buffer to disk by providing a filename.
:w callbacks.js
:ls
1 h "file.md" line 1
2 h "data.csv" line 1
3 h "code.rb" line 1
4 a "schema.sql" line 0
5 %a "callbacks.js" line 1
The file for buffer 5
now has a name, callbacks.js
.
If we are really observant, we'll also notice that the #
indicator is no longer present. Apparently we no longer have an alternate file. This is where our understanding of the alternate file as the previous buffer starts to fall apart.
It turns out that "an alternate file name is remembered for each window."[3] The new callbacks.js
file has no alternate file yet.
Back To One window
We have a split window situation. The top split is the buffer for schema.sql
, the bottom for callbacks.js
which has focus. If we are to quit out of the current window which contains the callbacks.js
buffer, the other window, which contains the schema.sql
buffer, will be able to reclaim the entire Vim window. We can do this with the quit
command.
:quit
:ls
1 h "file.md" line 1
2 h "data.csv" line 1
3 #h "code.rb" line 1
4 %a "schema.sql" line 1
5 h "callbacks.js" line 0
Listing the buffers reveals that the schema.sql
buffer is the only active buffer now. The callbacks.js
buffer is now hidden. Something more interesting is revealed though. The alternate file indicator is back, but perhaps not where we might have expected it. The alternate file is now code.rb
. Remember above where we said that each window remembers its own alternate file. The window for buffer 4
had code.rb
as its alternate file all along. Now that it is the window in focus, code.rb
is again the alternate file.
In Summary
We should now have a pretty solid handle on how to access and read the buffer list. We learned about each piece of information contained in the buffer list. In particular, we explored the most common indicators and saw the contexts in which they occur. Perhaps the most interesting part was the alternate file and the nuances of its definition.
In Part 2 of this series we will take a deeper dive into the buffer list. We will see how we can navigate between buffers based on the buffer list. Up to now the concept of an unloaded buffer has been obscured. We will take a look at some types of buffers that Vim keeps unloaded and see how we can unload buffers ourselves.
[1]: Unmodified buffers can be hidden by default. Modified buffers, however, depend on the hidden
option. If hidden
is on, then modified buffers can be hidden. If it is off, then Vim will raise an error and not let the buffer be hidden until saving. By default hidden
is set to off, but most .vimrc
configurations have hidden
set to on. This is also what sensible.vim
does. I recommend having set hidden
somewhere in your Vim configuration.
[2]: The alternate buffer indicator is not a meaningless distinction. We can quickly switch to the alternate buffer by hitting CTRL-^
or by typing :e #
. This comes in particularly handy when you are jumping back and forth between a file with some code and its unit tests.
[3]: See :help alternate-file
for more details.