BUFFERS
Learning Vim
Chapter 6 — Buffers, Windows, and Tabs
Three Concepts — One Mental Model
Vim organises its workspace around three distinct but related concepts. Conflating them causes confusion; keeping them clear makes everything else click into place.
Buffer
An in-memory copy of a file (or unnamed content). Editing happens here. Nothing is written to disk until you explicitly save.
Think of it as: the document.
Window
A viewport into a buffer. You can have many windows open simultaneously, and multiple windows can display the same buffer — changes in one are immediately visible in all.
Think of it as: the pane on screen.
Tab page
A collection of windows. Each tab has its own window layout. Tabs are not like browser tabs (one file each) — they are more like desktop workspaces.
Think of it as: a layout preset.
The key insight: closing a window does not delete a buffer. If you split a window to view two files and then close one of the windows, both files are still in memory — only the view has been removed. You can return to any buffer in the list at any time.
Buffers
Opening files into buffers
Every file you open in Vim becomes a buffer. Whether you open them from the command line or from within Vim, they all live in the buffer list.
# From the shell — open multiple files at once: vim file1.py file2.py file3.py # all three become buffers # From within Vim — add a new buffer: :e newfile.txt # opens file in current window :e . # open the directory browser
Listing buffers
:ls # list all open buffers (also :buffers or :files)
The output shows each buffer's number, status flags, name, and current line:
1 %a "app.py" line 42
2 h "config.yaml" line 1
3 h "tests/test_app.py" line 18
4 #a "utils.py" line 7
Reading the status flags:
| Flag | Meaning |
|---|---|
% | Current buffer (the one displayed in the active window) |
# | Alternate buffer — the one you were in before switching. Ctrl+^ toggles between current and alternate. |
a | Active — loaded and visible in a window |
h | Hidden — loaded in memory but not currently displayed |
+ | Modified — has unsaved changes |
- | Not modifiable (read-only) |
= | Read-only |
Switching between buffers
:bn # next buffer in the list (:bnext) :bp # previous buffer (:bprevious) :b 3 # switch to buffer number 3 :b config # switch to buffer whose name contains "config" (partial match) :bf # first buffer in the list :bl # last buffer in the list Ctrl+^ # toggle between current and alternate (#) buffer — fastest switch
:b you can press Tab to cycle through buffer names. Start typing a few characters of the filename first to narrow the list: :b con<Tab> might complete to :b config.yaml.
Deleting and unloading buffers
:bd # delete (unload) the current buffer :bd 3 # delete buffer number 3 :bd! # force-delete even if buffer has unsaved changes :bw # wipe buffer — removes it from the list entirely (vs just unloading) %bd # delete ALL buffers (useful for a clean slate)
:bd vs :bw: :bd (delete) unloads the buffer from memory but keeps it in the buffer list marked as unloaded — you can reload it. :bw (wipe) removes it from the list completely, including any marks, the jump list entry, and its history. For most purposes, :bd is sufficient.
Reloading a file from disk
:e! # reload current file, discarding all unsaved changes :e # reload if file changed on disk (fails if unsaved changes)
The directory browser
Opening a directory with :e . launches Vim's built-in file browser (netrw). You can navigate the filesystem and open files without leaving Vim:
:e . # open current directory in netrw :e ~/projects # open a specific directory
Inside netrw: press Enter to open a file or descend into a directory, - to go up a level, D to delete, R to rename, and % to create a new file.
Windows — Splitting the Screen
Windows let you view multiple buffers (or multiple positions in the same buffer) simultaneously on screen. This is one of Vim's most productive features for comparing files, referencing documentation while you code, or working on related sections of a large file.
Creating splits
:split # horizontal split — opens another window to the SAME buffer :split file.py # horizontal split — opens a DIFFERENT file in the new window :vsplit # vertical split (side by side) of same buffer :vsplit file.py# vertical split opening a different file Ctrl+W s # shortcut for :split Ctrl+W v # shortcut for :vsplit Ctrl+W n # new empty horizontal split
Navigating between windows
All window navigation uses the Ctrl+W prefix followed by a direction or action key:
| Command | Action |
|---|---|
Ctrl+W w | Cycle to the next window (clockwise) |
Ctrl+W W | Cycle to the previous window (counter-clockwise) |
Ctrl+W h | Move to the window to the left |
Ctrl+W j | Move to the window below |
Ctrl+W k | Move to the window above |
Ctrl+W l | Move to the window to the right |
Ctrl+W t | Move to the top-left window |
Ctrl+W b | Move to the bottom-right window |
Ctrl+W h/j/k/l mirrors the HJKL movement from Chapter 3 — left, down, up, right. Once this mapping clicks it becomes completely natural.
Resizing windows
Ctrl+W = # make all windows equal size Ctrl+W _ # maximise current window height Ctrl+W | # maximise current window width Ctrl+W + # increase current window height by 1 line Ctrl+W - # decrease current window height by 1 line Ctrl+W > # increase current window width by 1 column Ctrl+W < # decrease current window width by 1 column 10Ctrl+W + # increase height by 10 lines (count prefix works) # Or set an exact size: :resize 30 # set current window height to 30 lines :vertical resize 80 # set width to 80 columns
Closing and rearranging windows
Ctrl+W c # close current window (buffer remains in list) Ctrl+W o # close all OTHER windows — only keep current one (":only") :q # quit current window (if only one window, quits Vim) :only # same as Ctrl+W o # Moving windows: Ctrl+W r # rotate windows downward/rightward Ctrl+W R # rotate upward/leftward Ctrl+W x # exchange current window with the next one Ctrl+W H # move current window to far left (makes it a full-height vertical split) Ctrl+W J # move current window to bottom (full-width horizontal split) Ctrl+W K # move current window to top Ctrl+W L # move current window to far right
Opening a new buffer in a split
:sp other.py # open other.py in a horizontal split :vsp other.py # open in a vertical split :sb 3 # open buffer 3 in a horizontal split :vert sb 3 # open buffer 3 in a vertical split
Tab Pages
Tab pages are a layer above windows. Each tab holds its own set of windows and their layout. They are useful for organising completely separate workspaces — for example, one tab for source files and another for documentation — rather than for switching between individual files (that is what buffers and Ctrl+^ are for).
:tabnew # open a new empty tab :tabnew file.py# open file in a new tab :tabclose # close the current tab :tabnext # go to next tab (also gt in Normal mode) :tabprevious # go to previous tab (also gT in Normal mode) gt # next tab gT # previous tab 2gt # jump to tab 2 :tabs # list all tabs and their windows
Reading External File Content
The :r (read) command inserts the contents of another file directly into the current buffer at the cursor position. This is useful for inserting licence headers, boilerplate code, templates, or any file content you want to pull in without leaving Vim.
:r licence.txt # insert contents of licence.txt BELOW the current line :0r header.py # insert at the very top of the file (line 0 = before line 1) :-1r file.txt # insert one line ABOVE the cursor :$r footer.txt # insert at the very end of the file :42r snippet.py # insert after line 42 specifically
Tab completion works with :r just as with :e — start typing the path and press Tab to complete it.
Reading command output into the buffer
A powerful variant: :r! executes a shell command and inserts its output into the buffer:
:r! date # insert today's date at the cursor position :r! ls *.py # insert a list of Python files :r! cat README.md # insert contents of README (same as :r but via shell) :r! python3 -c "import sys; print(sys.version)" # insert Python version string
Practical Workflow — Comparing Two Files
A common real-world use case: comparing two versions of a file or checking how a config value is used in two different scripts.
Workflow — side-by-side file comparison
vimdiff file1.py file2.py. This automatically splits the screen, enables scrollbind, and colour-highlights the differences between the two files. Navigate between differences with ]c (next change) and [c (previous change).
Practical Workflow — Adding a Licence Header
Workflow — inserting a licence header with :r
Commands Introduced in This Chapter
Chapter 6 — Command Reference
:buffers, :files)