维护层,核查于 2026-09-01。 在 Vim 中,制表符和标签页是互不相关的功能。本指南依据当前 Vim 与 Neovim 文档分别解释两者。2019 年完整导出内容作为带日期的存档保留在文末,不作为现行配置建议。
最实用的心智模型很简单:缓冲区保存文本,窗口显示缓冲区,标签页保存一组窗口布局,而会话可以恢复更大的编辑工作区。分清这些对象后,打开、导航、关闭、映射和修复缩进都会变得可预测。
Table of Contents
“Tab”的两种含义
Vim 在两个不同意义上使用“Tab”一词:
- 制表符是文本中的水平制表字节 9。
tabstop、softtabstop、shiftwidth和expandtab等选项控制缩进的显示或输入方式。 - 标签页是一个或多个窗口的集合。它是一种工作区布局,不是文件容器,也不是缓冲区。
在插入模式按键盘 Tab 键不会创建标签页;同样,:tabnew 也不会改变缩进。把这两种含义分开,就能化解原始笔记中的大部分歧义。
缓冲区、窗口、标签页与会话
| 对象 | 表示什么 | 如何检查 | 常用操作 |
|---|---|---|---|
| 缓冲区 | 加载到内存中的文本,通常来自文件 | :ls | :buffer {name-or-number} |
| 窗口 | 一个缓冲区的视口 | :tabs 或 CTRL-W w | :split、:vsplit、:close |
| 标签页 | 一组窗口 | :tabs | :tabedit、gt、:tabclose |
| 会话 | 恢复窗口、标签页与选定状态的 Vim 脚本 | :set sessionoptions? | :mksession、vim -S |
同一个缓冲区可以出现在多个窗口中,甚至出现在不同标签页的窗口中。因此,关闭一个窗口不一定会删除它的缓冲区,关闭标签页也不等于“关闭一个文件”。
可复现的双工作区练习
先用一个文件启动 Vim 或 Neovim:
vim README.md
然后只用内置命令构建两组布局:
:vsplit src/main.py
:tabedit tests/test_main.py
:split tests/test_cli.py
:tabs
结果如下:
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
用 gt 和 gT 在标签页之间移动。在一个标签页内,用 CTRL-W h、CTRL-W j、CTRL-W k 和 CTRL-W l 在窗口间移动。运行 :ls,可以独立于可见布局查看缓冲区。
文件名只是示例;请替换成一次性测试仓库中实际存在的文件。如果指定文件不存在,Vim 会为该名称创建一个尚未保存的新缓冲区。
打开、导航、检查和移动标签页
这些内置命令足以覆盖大多数标签页操作:
:tabedit path/to/file
:tabnew
:tabs
:tabnext
:tabprevious
:tabfirst
:tablast
:tabmove 0
:tabmove
:tabedit {file}新建标签页,并在其中编辑该文件。:tabnew新建带空窗口的标签页。:tabs列出全部标签页及其窗口;加号表示存在已修改缓冲区。gt和:tabnext前往下一标签页;gT和:tabprevious前往上一标签页。{count}gt(例如2gt)按当前编号选择标签页。:tabmove 0把当前标签页移到开头;:tabmove把它移到末尾。
打开、关闭或移动标签页后,标签页编号可能变化。应把编号理解为当前位置,而不是永久标识符。
直接操作窗口与缓冲区
需要另一个视图时使用窗口,需要切换文本时使用缓冲区命令:
:split path/to/file
:vsplit path/to/file
:close
:only
:ls
:buffer 3
:bnext
:bprevious
:bdelete 3
:close 移除当前窗口。:only 会在能够安全关闭时,关闭当前标签页中的其他窗口。这两个命令都不表示“删除文件”。
:buffer、:bnext 和 :bprevious 改变当前窗口显示的缓冲区。:bdelete 从缓冲区列表移除一个缓冲区,并关闭显示它的窗口;不加 ! 时,它会拒绝放弃未保存的修改。若目标只是移除一个视图,应改用 :close。
安全地关闭正确对象
不带 ! 的命令通常是更安全的起点:
| 意图 | 命令 | 效果 |
|---|---|---|
| 保存当前缓冲区 | :write | 把当前缓冲区写入文件 |
| 保存所有已修改缓冲区 | :wall | 尝试写入所有已修改缓冲区 |
| 关闭一个窗口 | :close | 尽可能保留其他视图和缓冲区 |
| 关闭当前标签页 | :tabclose | 关闭其中全部窗口;拒绝不安全的放弃操作 |
| 移除一个缓冲区 | :bdelete {name} | 从列表移除;拒绝丢失未保存内容 |
| 退出当前窗口 | :quit | 仅当它是最后一个窗口时退出 Vim |
不要习惯性添加 !:对 :quit! 或 :bdelete! 等命令而言,它可能丢弃修改。Vim 报告未保存缓冲区时,先检查 :ls;需要保留就用 :write,否则明确决定是否放弃。
简化大型布局前,可以使用保守顺序:
:ls
:wall
:tabs
:tabclose
对于未命名、只读或因其他原因不可写的缓冲区,:wall 仍可能失败;应阅读消息,不要假定所有内容已经保存。
保存和恢复会话
会话是可执行的 Vim 脚本,它依据 sessionoptions 恢复窗口布局、标签页、缓冲区与选定选项。
先检查策略,再保存当前工作区:
:set sessionoptions?
:wall
:mksession Session.vim
不带 bang 的形式会拒绝覆盖已有会话文件。明确需要更新该文件时,使用:
:wall
:mksession! Session.vim
从 shell 恢复:
vim -S Session.vim
使用 Neovim 时,改用 nvim -S Session.vim。
或在 Vim 内恢复:
:source Session.vim
除非从 sessionoptions 移除了 tabpages,会话会包含全部标签页。它不会恢复每一种状态,插件行为也可能产生额外效果。会话会作为代码被 source,因此只加载可信文件,并在加载前审阅。若会话含有无意公开的私人路径,不要提交到版本库。
添加最小且可检查的映射
添加映射前,先学会 gt、gT 和 Ex 命令。如果配置确实需要便于记忆的按键,应使用非递归普通模式映射,并让安全、不带 bang 的命令保持清晰可见:
let mapleader = " "
nnoremap <silent> <leader>tn <Cmd>tabnew<CR>
nnoremap <silent> <leader>tc <Cmd>tabclose<CR>
nnoremap <silent> <leader>tl <Cmd>tabs<CR>
检查意外映射以及最后定义它的脚本:
:verbose nmap gt
:verbose nmap <leader>tc
Neovim Lua 配置中的等价显式映射如下:
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" })
不要仅为了标签页导航就安装庞大的配置。先使用内置功能,只添加自己能够解释的映射,并用 :verbose nmap 检查冲突。
配置制表符与缩进
这些都是缓冲区局部选项,因此应在受影响的文件中检查:
:setlocal tabstop? shiftwidth? softtabstop? expandtab?
tabstop控制一个实际制表符显示时占用的列数。shiftwidth控制>>、<<与自动缩进等操作的缩进步长。softtabstop控制编辑时 Tab 和 Backspace 的行为。expandtab让常见插入和缩进操作插入空格,而不是实际制表符。
常见的四空格、纯空格策略是:
setlocal expandtab
setlocal tabstop=4
setlocal shiftwidth=4
setlocal softtabstop=4
使用制表符的项目可能改为:
setlocal noexpandtab
setlocal tabstop=8
setlocal shiftwidth=8
setlocal softtabstop=8
这些只是示例,并非通用规则。应遵循仓库现有风格。修改 tabstop 只会改变实际制表符的显示方式,不会重写文件。
谨慎检查并规范化已有文件
修改前先显示不可见空白:
:setlocal list
:setlocal listchars=tab:>-,trail:·
选定项目策略后,可以先在内存中进行纯空格转换:
:setlocal expandtab tabstop=4 shiftwidth=4 softtabstop=4
:%retab
不带 ! 时,:retab 会处理包含制表符的空白序列;启用 expandtab 后,它会用适量空格替换制表符,同时保持显示对齐。不过,它仍可能修改字符串或数据中的制表符,因此接受修改前必须检查缓冲区和版本控制 diff。若内存中的转换不正确,可用 u 撤销。
然后明确写入,并检查仓库 diff:
:write
存档中“全局把八个实际空格替换成四个”的命令不会替换制表符,还可能破坏有意的对齐。保留它是为了溯源,不应作为通用修复方法。
应用文件类型专属规则
把持久规则放到自己控制的配置中。以下 Vimscript 示例使用 augroup,避免重新加载配置时重复创建自动命令:
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
C 这一行只是示例;许多 C 项目使用空格或其他宽度。应匹配具体项目,而不是只依据语言名称。
选项反复变化时,让 Vim 告诉你当前值最后在哪里设置:
:verbose setlocal expandtab?
:verbose setlocal tabstop?
:verbose setlocal shiftwidth?
:verbose setlocal softtabstop?
这样通常能找出覆盖预期值的文件类型插件、自动命令、modeline 或其他配置层。
诊断 Vim 与其他编辑器的不一致
文件在 Vim 中对齐、在 Notepad++ 或其他编辑器中却错乱时,先诊断再转换:
- 运行
:setlocal list,区分实际制表符与空格。 - 查询
tabstop、shiftwidth、softtabstop和expandtab。 - 检查另一编辑器的制表符显示宽度,以及 Tab 键插入制表符还是空格。
- 从已有文件和工具确认仓库预期规范。
- 只有文件确实不一致时才转换,然后检查 diff。
显示宽度与文件内容是两个不同问题。两个编辑器可能用不同宽度显示同一个实际制表符,而两个外观看似相同的文件也可能包含不同字节。
故障排查
gt没有反应: 可能只有一个标签页,也可能映射覆盖了它。运行:tabs和:verbose nmap gt。- 关闭标签页后,文件仍出现在
:ls: 窗口关闭后,缓冲区仍可能处于已加载或隐藏状态。只有确实要移除缓冲区时才用:bdelete {name}。 :bdelete影响了多个视图: 多个窗口显示着同一缓冲区。若只想移除一个窗口,请用:close。- Vim 报告“No write since last change”: 这是安全检查。保存缓冲区,或明确决定是否放弃修改。
- 恢复会话后映射或选项变化:
source前检查sessionoptions和生成的会话;会话可以恢复选定的全局状态。 - 标签看起来像文件名: 标签代表标签页中的窗口集合,并不表示某个缓冲区归它所有。
:%retab产生大量 diff: 撤销操作,确认原始tabstop,并检查制表符是否出现在数据或字符串中,再决定是否重试。
Vim 与 Neovim 一手文档
- Vim 源码手册:缓冲区与窗口
- Vim 源码手册:标签页
- Vim 源码手册:会话
- Vim 源码手册:映射
- Vim 源码手册:缩进选项
- Vim 源码手册:`:retab`
- Neovim 手册:缓冲区与窗口
- Neovim 手册:标签页
- Neovim 手册:会话
- Neovim 手册:映射
- Neovim Lua 指南:`vim.keymap.set`
- Neovim 手册:选项
---
2019 年原始存档(英文原文保留)
以下是完整的 WordPress 导出,包含原始元数据、用词、顺序、命令与标题;发布及最后修改日期均为 2019-04-21。它是历史材料,不是上方的现行建议。仅为仓库格式规范化了不可见的行末空白。
---
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
