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:

FlagMeaning
%Current buffer (the one displayed in the active window)
#Alternate buffer — the one you were in before switching. Ctrl+^ toggles between current and alternate.
aActive — loaded and visible in a window
hHidden — 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
Tab completion on buffer names: When typing :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
After :split
Window 1 — app.py (active)
────────────────────
Window 2 — app.py (same buffer)

After :vsplit config.yaml
Window 1 — app.py
Window 2 — config.yaml

Navigating between windows

All window navigation uses the Ctrl+W prefix followed by a direction or action key:

CommandAction
Ctrl+W wCycle to the next window (clockwise)
Ctrl+W WCycle to the previous window (counter-clockwise)
Ctrl+W hMove to the window to the left
Ctrl+W jMove to the window below
Ctrl+W kMove to the window above
Ctrl+W lMove to the window to the right
Ctrl+W tMove to the top-left window
Ctrl+W bMove to the bottom-right window
HJKL for window navigation: 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

1 vim app_v1.py Open the first file from the shell.
2 :vsplit app_v2.py Open the second file in a vertical split — both are now visible side by side.
3 Ctrl+W = Equalise the window widths so both get the same space.
4 Ctrl+W h / l Move between the two windows using HJKL navigation.
5 :set scrollbind Run in both windows (switch with Ctrl+W l, run again). Now scrolling one window scrolls both simultaneously — perfect for comparison.
6 Ctrl+W o When done comparing, close all other windows and return to a single view.
Vim's built-in diff mode: For a proper diff view with changes highlighted, open Vim in diff mode from the shell: 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

1 vim script.py Open the Python file that needs the header.
2 :0r licence.txt Insert the licence file content at the very top (line 0 = before line 1). Tab complete the filename.
3 gg Jump to the top to verify the header was inserted correctly.
4 :w Save the file.

Commands Introduced in This Chapter

Chapter 6 — Command Reference

Buffer operations
:lsList all open buffers (also :buffers, :files)
:bn / :bpNext / previous buffer
:bf / :blFirst / last buffer in the list
:b {n}Switch to buffer number n
:b {name}Switch to buffer matching name (partial, Tab-complete)
Ctrl+^Toggle between current and alternate (#) buffer
:bdDelete (unload) current buffer
:bd!Force-delete buffer with unsaved changes
:bwWipe buffer (remove from list entirely)
%bdDelete all buffers
:e!Reload current file from disk, discard changes
:e .Open directory browser (netrw)
Window splits
:split / :spHorizontal split (same or new buffer)
:vsplit / :vspVertical split (side by side)
Ctrl+W sHorizontal split (shortcut)
Ctrl+W vVertical split (shortcut)
Ctrl+W nNew empty horizontal split
:sb {n}Open buffer n in a horizontal split
:vert sb {n}Open buffer n in a vertical split
Window navigation
Ctrl+W w / WCycle to next / previous window
Ctrl+W h/j/k/lMove to window left / down / up / right
Ctrl+W t / bMove to top-left / bottom-right window
Window resizing
Ctrl+W =Equalise all window sizes
Ctrl+W _ / |Maximise current window height / width
Ctrl+W +/−Increase / decrease window height
Ctrl+W >/<Increase / decrease window width
:resize {n}Set window height to n lines
:vertical resize {n}Set window width to n columns
Window closing & rearranging
Ctrl+W cClose current window
Ctrl+W o / :onlyClose all other windows, keep only current
Ctrl+W r / RRotate windows down/right or up/left
Ctrl+W xExchange current window with next
Ctrl+W H/J/K/LMove window to far left/bottom/top/right
Tab pages
:tabnew / :tabnew {file}Open a new tab (empty or with file)
:tabcloseClose current tab
gt / gTGo to next / previous tab
{n}gtGo to tab number n
:tabsList all tabs and their windows
Reading files
:r {file}Insert file contents below current line
:0r {file}Insert at very top of file
:-1r {file}Insert one line above cursor
:{n}r {file}Insert after line n
:r! {cmd}Insert output of shell command into buffer
Diff mode
vimdiff f1 f2Open two files in diff mode (from shell)
:set scrollbindLink scrolling of multiple windows together
]c / [cJump to next / previous change in diff mode