Maintained layer, checked 2026-09-01. In Vim, a tab character and a tab page are unrelated features. This guide explains both, using current Vim and Neovim documentation. The complete 2019 export remains in a dated archive at the end and is not presented as current setup advice.
The most useful mental model is simple: a buffer holds text, a window displays a buffer, and a tab page holds a window layout. A session can restore a larger editing workspace. Once these objects are kept separate, opening, navigating, closing, mapping, and fixing indentation become predictable.
Table of Contents
Two Meanings of “Tab”
Vim uses the word in two distinct ways:
- A tab character is horizontal-tab byte 9 inside text. Options such as
tabstop,softtabstop,shiftwidth, andexpandtabcontrol how indentation is displayed or inserted. - A tab page is a collection of one or more windows. It is a workspace layout, not a file container and not a buffer.
Pressing the keyboard Tab key in Insert mode does not create a tab page. Likewise, :tabnew does not change indentation. Keeping these two meanings separate resolves much of the confusion in the original note.
Buffers, Windows, Tab Pages, and Sessions
| Object | What it represents | Inspect it | Typical action |
|---|---|---|---|
| Buffer | Text loaded into memory, usually from a file | :ls |
:buffer {name-or-number} |
| Window | A viewport onto one buffer | :tabs or CTRL-W w |
:split, :vsplit, :close |
| Tab page | One collection of windows | :tabs |
:tabedit, gt, :tabclose |
| Session | A Vim script that restores windows, tab pages, and selected state | :set sessionoptions? |
:mksession, vim -S |
The same buffer may appear in several windows, including windows on different tab pages. Closing one window therefore does not necessarily delete its buffer, and closing a tab page does not mean “close one file.”
A Reproducible Two-Workspace Exercise
Start Vim or Neovim with one file:
vim README.md
Then build two layouts using only built-in commands:
:vsplit src/main.py
:tabedit tests/test_main.py
:split tests/test_cli.py
:tabs
The result is:
Tab page 1
├── window: README.md buffer
└── window: src/main.py buffer
Tab page 2
├── window: tests/test_main.py buffer
└── window: tests/test_cli.py buffer
Use gt and gT to move between tab pages. Within one tab page, use CTRL-W h, CTRL-W j, CTRL-W k, and CTRL-W l to move among windows. Run :ls to see buffers independently of the visible layout.
The filenames are examples; substitute files that exist in a disposable repository. If a named file does not exist, Vim creates a new, unsaved buffer for that name.
Open, Navigate, Inspect, and Move Tab Pages
These built-in commands cover most tab-page work:
:tabedit path/to/file
:tabnew
:tabs
:tabnext
:tabprevious
:tabfirst
:tablast
:tabmove 0
:tabmove
:tabedit {file}opens a new tab page and edits the file there.:tabnewopens a new tab page with an empty window.:tabslists every tab page and its windows; a plus sign identifies a modified buffer.gtand:tabnextgo to the next tab page;gTand:tabpreviousgo to the previous one.{count}gt, such as2gt, selects a tab page by its current number.:tabmove 0moves the current tab page to the beginning;:tabmovemoves it to the end.
Tab-page numbers can change after a page is opened, closed, or moved. Treat them as current positions rather than permanent identifiers.
Work With Windows and Buffers Directly
Use a window when you need another view and a buffer command when you need different text:
:split path/to/file
:vsplit path/to/file
:close
:only
:ls
:buffer 3
:bnext
:bprevious
:bdelete 3
:close removes the current window. :only closes the other windows in the current tab page when it can do so safely. Neither command means “delete the file.”
:buffer, :bnext, and :bprevious change which buffer the current window displays. :bdelete removes a buffer from the buffer list and closes windows that display it; without !, it refuses to abandon unsaved changes. Use :close instead when the goal is only to remove one view.
Close the Right Thing Safely
The command without ! is normally the safer starting point:
| Intent | Command | What happens |
|---|---|---|
| Save the current buffer | :write |
Writes this buffer to its file |
| Save all changed buffers | :wall |
Attempts to write every changed buffer |
| Close one window | :close |
Keeps other views and buffers where possible |
| Close the current tab page | :tabclose |
Closes all its windows; refuses unsafe abandonment |
| Remove one buffer | :bdelete {name} |
Removes it from the list; refuses unsaved loss |
| Quit the current window | :quit |
Exits Vim only if it was the last window |
Avoid adding ! reflexively: with commands such as :quit! or :bdelete!, it can discard changes. When Vim reports an unsaved buffer, inspect :ls, use :write if the change is wanted, or make an explicit discard decision.
Before simplifying a large layout, a conservative sequence is:
:ls
:wall
:tabs
:tabclose
:wall can still fail for unnamed, read-only, or otherwise unwritable buffers, so read the messages rather than assuming everything was saved.
Save and Restore a Session
A session is an executable Vim script that can restore the window layout, tab pages, buffers, and selected options according to sessionoptions.
Inspect the policy and save the current workspace:
:set sessionoptions?
:wall
:mksession Session.vim
The non-bang form refuses to overwrite an existing session file. When intentionally updating that file, use:
:wall
:mksession! Session.vim
Restore it from the shell:
vim -S Session.vim
For Neovim, use nvim -S Session.vim instead.
Or restore it inside Vim:
:source Session.vim
Sessions include tab pages unless tabpages has been removed from sessionoptions. They do not restore every kind of state, and plugin behavior may add its own effects. Because a session is sourced as code, review it and load only session files you trust. Do not commit a session containing private paths unless those paths are intended to be public.
Add Minimal, Inspectable Mappings
Learn gt, gT, and the Ex commands before adding mappings. If a configuration needs mnemonic keys, use non-recursive normal-mode mappings and keep the safe, non-bang commands visible:
let mapleader = " "
nnoremap <silent> <leader>tn <Cmd>tabnew<CR>
nnoremap <silent> <leader>tc <Cmd>tabclose<CR>
nnoremap <silent> <leader>tl <Cmd>tabs<CR>
Inspect an unexpected mapping and the script that last defined it:
:verbose nmap gt
:verbose nmap <leader>tc
For a Neovim Lua configuration, the equivalent explicit mappings are:
vim.g.mapleader = " "
vim.keymap.set("n", "<leader>tn", "<cmd>tabnew<cr>", { desc = "New tab page" })
vim.keymap.set("n", "<leader>tc", "<cmd>tabclose<cr>", { desc = "Close tab page" })
vim.keymap.set("n", "<leader>tl", "<cmd>tabs<cr>", { desc = "List tab pages" })
Do not install a large configuration merely to obtain tab navigation. Start with built-ins, add mappings you can explain, and check for collisions with :verbose nmap.
Configure Tab Characters and Indentation
These are buffer-local options, so inspect them in the affected file:
:setlocal tabstop? shiftwidth? softtabstop? expandtab?
tabstopcontrols the displayed columns occupied by a literal tab character.shiftwidthcontrols indentation steps used by commands such as>>,<<, and automatic indentation.softtabstopcontrols how Tab and Backspace behave while editing.expandtabinserts spaces instead of a literal tab in common insertion and indentation operations.
A conventional four-space, spaces-only policy is:
setlocal expandtab
setlocal tabstop=4
setlocal shiftwidth=4
setlocal softtabstop=4
A tab-based project might instead require:
setlocal noexpandtab
setlocal tabstop=8
setlocal shiftwidth=8
setlocal softtabstop=8
These are examples, not universal rules. Follow the repository’s existing style. Changing tabstop changes how literal tabs look; it does not rewrite the file.
Inspect and Normalize an Existing File Carefully
Make invisible whitespace visible before changing anything:
:setlocal list
:setlocal listchars=tab:>-,trail:·
After choosing the project policy, a spaces-only conversion can be performed in memory:
:setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4
:%retab
Without !, :retab targets whitespace sequences that contain a tab; with expandtab, it replaces tabs with the appropriate number of spaces while preserving displayed alignment. It can still alter tabs inside strings or data, so inspect the buffer and version-control diff before accepting the change. Use u to undo an unwanted in-memory conversion.
Then write deliberately and inspect the repository diff:
:write
The archived command that globally replaces eight literal spaces with four does not replace tab characters and can damage intentional alignment. It is preserved for provenance, not recommended as a general repair.
Apply Filetype-Specific Rules
Place durable rules in a configuration you control. This Vimscript example uses an augroup so re-sourcing the file does not duplicate autocommands:
augroup indentation_rules
autocmd!
autocmd FileType python setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4
autocmd FileType c setlocal noexpandtab tabstop=8 shiftwidth=8 softtabstop=8
augroup END
The C line is only an example; many C projects use spaces or a different width. Match the project instead of the language name alone.
When an option keeps changing, ask Vim where its current value was last set:
:verbose setlocal expandtab?
:verbose setlocal tabstop?
:verbose setlocal shiftwidth?
:verbose setlocal softtabstop?
This often identifies a filetype plugin, autocommand, modeline, or configuration layer that overrode the expected value.
Diagnose Vim and Other-Editor Inconsistency
When a file aligns in Vim but not in Notepad++ or another editor, diagnose before converting:
- Run
:setlocal listto distinguish literal tabs from spaces. - Query
tabstop,shiftwidth,softtabstop, andexpandtab. - Check the other editor’s tab display width and whether its Tab key inserts tabs or spaces.
- Confirm the repository’s intended convention from existing files and tooling.
- Convert only if the file is actually inconsistent; then inspect the diff.
Display width and file content are different problems. Two editors can display the same literal tab differently, while two files that look identical can contain different bytes.
Troubleshooting
gtdoes nothing: there may be only one tab page, or a mapping may override it. Run:tabsand:verbose nmap gt.- Closing a tab did not remove a file from
:ls: the buffer can remain loaded or hidden after its windows close. Use:bdelete {name}only if removing the buffer is the intent. :bdeleteaffected several views: several windows were displaying the same buffer. Use:closeto remove just one window.- Vim reports “No write since last change”: this is a safety check. Save the buffer or explicitly decide whether the change should be discarded.
- A restored session changes mappings or options: inspect
sessionoptionsand the generated session before sourcing it; sessions can restore selected global state. - Tab labels appear to name files: a label represents a tab page’s window collection, not ownership of one buffer.
:%retabproduced a large diff: undo it, confirm the originaltabstop, and check whether tabs occur in data or strings before retrying.
Primary Vim and Neovim Documentation
- Vim source manual: buffers and windows
- Vim source manual: tab pages
- Vim source manual: sessions
- Vim source manual: mappings
- Vim source manual: indentation options
- Vim source manual: `:retab`
- Neovim manual: buffers and windows
- Neovim manual: tab pages
- Neovim manual: sessions
- Neovim manual: mappings
- Neovim Lua guide: `vim.keymap.set`
- Neovim manual: options
—
Original 2019 Archive (Verbatim)
The following is the complete WordPress export, including its original metadata, wording, order, commands, and headings. It was published and last modified on 2019-04-21. It is historical material, not the maintained recommendation above. Only invisible trailing whitespace has been normalized for repository formatting.
---
id: 1876
title: 'Vim Configuration and Tab Inconsistency Between Vim and Notepad++'
slug: 'vim-configuration-and-tab-inconsistency-between-vim-and-notepad'
date: '2019-04-21T03:42:35'
modified: '2019-04-21T03:42:42'
status: 'publish'
link: 'https://blog.lazying.art/en/html/computer_internet/unix_linux/v_ivim/1876/vim-configuration-and-tab-inconsistency-between-vim-and-notepad.html'
author: 'Lachlan Chen'
categories:
- 'VI&VIM'
---
General
A completed configuration can be installed by running this
git clone –depth=1 https://github.com/amix/vimrc.git ~/.vim_runtime
sh ~/.vim_runtime/install_awesome_vimrc.sh
If you want to add some customized configurations,
vim ~/.vim_runtime/my_configs.vim
Some sample configuration of your vim
set shiftwidth=4 ” used by >>, << and tab.
set tabstop=4 ” number of space characters used when displaying TAB
set expandtab ” replace TAB by spaces
set softtabstop=4 ” will erase 4 spaces at once when using BS in the begining of lines only
If this is an existed file, you might run this to sort out your file
:%retab
Python
C
Vim and Notepad++ Inconsistency
To solve the inconsistency, you might want to use
:%s/ / /g
