This can be a hard or an easy topic, mostly because if someone is coming from a modern GUI editor, they have a different meaning. For simplicity, I’ll go from the lower layer to the top layer (nope, that’s not how it works, but kind of).

Buffer

Buffer is something that has content. If we open a file, it will be in a buffer, if we open a new buffer, it will be in a new buffer. Even if we can see only one file, we can have a dozen of buffers.

We can list our buffers with :buffers or :ls (they are the same). The output can be confusing first, but it has a nice structure. Here is an example from :help:

1 #h   "/test/text"    line 1 ~
2u     "asdf"          line 0 ~
3 %a + "version.c"     line 1 ~

Each buffer has a unique number, this number does not change, even if you delete a buffer, that id will not be reused (until vim is restarted). This is useful because with :b N we can jump to a specific buffer always and we know it will always be the one we want, because the that id unique.

After the unique number, we have a five columns.

The first column can be empty or u. A buffer can be hidden, but let’s just ignore it now. That u on the second buffer means, it’s an unlisted/hidden buffer. Hidden buffers are listed only with :buffers!. When we open :help, it will be in a buffer. It can be confusing because it’s not listed with :buffers, but with :buffers! it will be in the list.

The second column can be %, #, or empty. The % means that buffer is in the current window. # means it’s the alternate buffer. Alternate buffer is the one we used before. It can be useful when we want to jump back and forth between two buffers. With :e # or C-^ we open the alternate buffer, and at the same time, the buffer we used before the jump will be the new alternate buffer.

The third column indicated the visibility, a as active (it is loaded and visible) and h as hidden (it’s loaded, but not displayed in a window).

The fourth column is a flag, let’s call it accessibility flag, I don’t have better word for it. It can have more values:

  • -: Buffer with modifiable off.
  • =: Buffer is read only.
  • R: Terminal buffer with a running job.
  • F: Terminal buffer with a finished job.
  • ?: Terminal buffer without a job.

The first two can be confusing. If a buffer is in a readonly mode, we can still edit the content, but can’t save it, while a buffer with modifiable set to off, we can’t even edit the buffer. Mostly used by plugins (like nvim-tree). Another good example is the :help buffer, you can’t change the content.

The last column is a simple state.

  • +: Modified buffer.
  • x: Buffer with read errors.

We can do a lot of things with buffers, that’s a fundamental element of vim.

Window

Window is something we can see. A window is graphical representation of a buffer. A window always has a buffer, but a buffer can be visible in multiple windows. It sounds strange, but that’s a window. We can create a new window with :new, :vnew, :split, or :vsplit. Windows are independent from each other and from buffers if we are talking about movement. We can load another buffer in the current window anytime. If we navigating in the content of a window, the other window will stand still. Here comes a tricky part, if we have two windows, both of them shows the same buffer:

  1. We can navigate in the content in one window and the other window will not follow the movement.
  2. If we edit the content in one window it will be in both windows because it’s not the same as opening the same file twice, if you change the buffer, it will be changed in both windows without saving because they are the same buffers, think about them as a pointer, a window shows a buffer which is a pointer and multiple windows can use the same pointer (buffer).

By default, movement between windows is the same as navigating in a buffer, but with a C-w prefix.

  • C-w j: Move to the window below current one.
  • C-w k: Move to the window above current one.
  • C-w h: Move to the window left of current one.
  • C-w l: Move to the window right of current one.

We can rotate and rearrange windows:

  • C-w J: Move current window to the very bottom.
  • C-w K: Move current window to the very top.
  • C-w H: Move current window to the far left.
  • C-w L: Move current window to the far right.

We can do a lot more, I would suggest to read more about windows online, there are a lot of good articles, if I want to mention how to resize and other things, this post would be a complete booklet.

Tabs

Tabs are useful if we want to use different window layouts with the same or different buffers. When we open vim, we have one buffer, in one window, in one tab. We can have a 50-50 split in one tab with two files (buffers) and on a new tab we can have one of the files (buffers) and a terminal in a horizontal 90-30 split.

To understand tabs, the first step is to forget how tabs behave in your browser or GUI editors, they are a simple collection/layout of windows.

Example

Most of the time I have two tabs:

  1. Code editing, mostly with a 50-50 vertical split (main file + test file).
  2. Octo.

Sometimes I have more tabs, like a new tab for GitHub Dashboard, or if I have to live without tmux or tiling window manager, a new tab with a terminal. Tabs can be useful when I do pull request reviews and I want to check the code somewhere else, I’ll open a new tab and do my research without the fear of destroying the windows I have open for the review.