在 vim 中,tab 字符默认的显示宽度是 8 个空格。在终端打开 vim,多按几次 Tab 键,很容易就超出终端的显示宽度。为了良好的编码体验,需要调整 tab 字符的显示宽度。如果不想插入 tab 字符,还需要把 tab 字符替换成空格。
可以通过配置 tabstop 选项、softtabstop 选项、expandtab 选项来修改 tab 字符的显示和按 Tab 键的行为:
- tabstop 选项只修改 tab 字符的显示宽度,不修改按 Tab 键的行为
- softtabstop 选项修改按 Tab 键的行为,不修改 tab 字符的显示宽度。具体行为跟 tabstop 选项值有关
- expandtab 选项把插入的 tab 字符替换成特定数目的空格。具体空格数目跟 tabstop 选项值有关
tabstop 选项
在 vim 中,使用 :help tabstop
命令查看 tabstop 选项的说明如下:
'tabstop' 'ts' number (default 8)
Number of spaces that ain the file counts for.
Note: Setting 'tabstop' to any other value than 8 can make your file appear wrong in many places (e.g., when printing it).
即,tabstop 选项设置 tab 字符的显示宽度为多少个空格,默认值是 8。可以使用下面命令修改这个选项值为 4:
:set tabstop=4
tabstop 选项可以简写为 ts,:set tabstop=4
命令和 :set ts=4
命令是等效的。
上面说明提到,如果设置 tabstop 选项为 8 之外的值,可能会使文件在许多地方(例如,在打印时)显示错误。
例如,设置 tabstop 选项值为 4,在 vim 中使用 tab 字符进行列对齐,看起来排版正常,保存文件。但是在其他显示 tab 字符为 8 个空格的软件上查看这个文件,原本对齐的列可能就会错乱,没对齐。
在 vim 帮助文档的各个小节中,如 :help usr_30
的 Tabs and spaces 小节、:help usr_25
的 Indents and tabs 小节,都多次建议要保持 tabstop 选项值为 8 不变。
在 :help tabstop
命令的说明中,只有设置了 expandtab 选项,把 tab 字符替换为空格的情况下,才建议修改 tabstop 选项为其他值。此时,在 vim 的插入模式下,按下 Tab 键,插入的是空格,而不是 tab 字符,保存文件后,用其他软件打开这个文件,看不到 tab 字符,自然不会因为 tab 字符的显示宽度不同而导致排版异常。
注意:tabstop 选项只修改 tab 字符在 vim 中的显示宽度,不修改插入模式下按 Tab 键,vim 插入的是 tab 字符,还是插入特定数目的空格。
softtabstop 选项
在 vim 中,使用 :help softtabstop
命令查看 softtabstop 选项的说明如下:
'softtabstop' 'sts' number (default 0)
Number of spaces that acounts for while performing editing operations, like inserting a or using . It "feels" like s are being inserted, while in fact a mix of spaces and s is used.
When 'sts' is zero, this feature is off.
When 'sts' is negative, the value of 'shiftwidth' is used.
即,softtabstop 选项(可以简写为 sts)会影响 vim 在插入模式下按 Tab 键所实际得到的字符,可能是插入特定数目的空格,也可能是插入一个 tab 字符。
具体使用时,会受到 tabstop 选项和 expandtab 选项的影响。可以使用下面命令修改这个选项值为 4:
:set softtabstop=4
设置之后,在 tabstop 选项值为 8,且没有设置 expandtab 选项的情况下,第一次按 Tab 键,vim 会插入 4 个空格,而不是插入一个 tab 字符。
之后,第二次按 Tab 键,vim 会删除前面的 4 个空格,然后插入一个 tab 字符。由于 tabstop 选项值是 8,界面上会看到光标继续往前移动了 4 个空白字符,像是又插入了 4 个空格,但实际上两次按 Tab 键,只插入一个 tab 字符。
从写入文件的字符和字节数来说,第一个按 Tab 键,写入 4 个空格,也就是四个字节,第二次按 Tab 键,会删掉之前写入的 4 个空格,再写入一个 tab 字符,只有一个字节,写入文件的字节数变少了。
虽然从界面上看,显示了 8 个空白字符,但这只是一个 tab 字符的显示宽度。可以执行下面命令让空格和 tab 字符可见,再进行测试,就能清楚地看到这一点:
:set list
:set listchars=tab:>~,space=.
另外,softtabstop 选项也会影响 Backspace 键删除连续多个空格和删除 tab 字符的行为。
在上面的场景中,第一次按 Tab 键,vim 会插入 4 个空格,此时可以用 Backspace 键一次性删除这 4 个空格。即,只按一次 Backspace 键,就能删除这 4 个空格。
当按下两次 Tab 键,插入一个 tab 字符后,按下 Backspace 键,其实是把这个 tab 字符替换成 4 个空格。
从界面上看,光标往前移动了 4 个空白字符,但是文件的字节数实际上变多了。
比较特别的是,在插入模式下,手动输入 4 个空格,此时按 Backspace 键只能删除一个空格。但是手动输入 4 个空格后,退出插入模式,再进入插入模式,就能用 Backspace 键一次性删除这 4 个空格。
当 tabstop 选项值是 4,softtabstop 选项值是 4,且没有设置 expandtab 选项时,每次按 Tab 键,都是插入一个 tab 字符,不会再插入任何空格。
如前面说明,vim 建议不要进行这样的设置,避免用 Tab 键插入 tab 字符进行对齐缩进时,在其他软件上查看文件可能会出现排版异常。
当 softtabstop 选项值小于 0 时,其真实值会被设置成 shiftwidth 选项的值。这个特性有助于保持和自动缩进的排版一致。后面会具体说明。
注意:softtabstop 选项影响 vim 在插入模式下按 Tab 键所实际得到的字符,不改变 vim 中 tab 字符的显示宽度,tab 字符始终显示为 tabstop 指定的宽度。
当 softtabstop 选项值小于 tabstop 选项值时,第一次按 Tab 键,会插入 softtabstop 选项值对应的多个空格,当插入的空格个数到达 tabstop 指定的宽度时,会删除插入的空格,替换成一个 tab 字符。
expandtab 选项
在 vim 中,使用 :help expandtab
命令查看 expandtab 选项的说明如下:
'expandtab' 'et' boolean (default off)
In Insert mode: Use the appropriate number of spaces to insert a.
To insert a real tab when 'expandtab' is on, use CTRL-V.
即,设置 expandtab 选项后,在插入模式下,会把按 Tab 键所插入的 tab 字符替换为合适数目的空格。如果确实要插入 tab 字符,需要按 CTRL-V 键,再按 Tab 键。
可以使用下面命令来开启这个选项:
:set expandtab
设置之后,会把一个 tab 字符替换成 tabstop 选项值对应的多个空格。例如,tabstop 选项值为 8,那么插入一个 tab 字符,会被替换成 8 个空格。
需要注意的是,对于 softtabstop 选项值为 4,tabstop 选项值为 8,且没有设置 expandtab 选项的场景来说,第一次按 Tab 键,插入的是 4 个空格,并没有插入 tab 字符,没有发生替换 tab 字符为空格的情况,并不是说设置 softtabstop 选项值为 4,expandtab 选项会基于 softtabstop 选项值来把 tab 字符替换成 4 个空格。
当按两次 Tab 键,插入一个 tab 字符后,expandtab 选项还是基于 tabstop 选项值把 tab 字符替换成 8 个空格。
注意:设置 expandtab 选项只能把新插入的 tab 字符替换成特定数目的空格,不影响文件中已有的 tab 字符。即,文件已有的 tab 字符会保持不变。
配置插入 tab 字符时自动替换成空格
基于前面的说明,如果要把插入的 tab 字符自动替换成空格,可以在 ~/.vimrc
文件中添加下面的配置:
" 自动缩进时,缩进长度为4
set shiftwidth=4
" 输入Tab字符时,自动替换成空格
set expandtab
" softtabstop的值为负数,会使用shiftwidth的值,两者保持一致,方便统一缩进.
set softtabstop=-1
这里没有配置 tabstop 选项值,保持这个值为默认的 8 不变。配置了 expandtab 选项,按 Tab 键所插入的 tab 字符会被替换成 8 个空格。
上面配置 shiftwidth 选项值为 4,该选项设置自动缩进的空格个数。同时配置 softtabstop 选项值为 -1,那么该选项值会等于 shiftwidth 选项值,也是 4。
第一次按 Tab 键,会插入 4 个空格,和自动缩进的空格个数保持一致,方便统一缩进,不会出现自动缩进插入 8 个空格,而手动按 Tab 键只插入 4 个空格,缩进没有对齐的情况。
即,为了保持对齐缩进一致,手动按 Tab 键所插入的空格个数要跟自动缩进的空格个数相同,也就是 softtabstop 选项值要跟 shiftwidth 选项值相等。
设置 softtabstop 选项值为负数就可以满足这个需求,后续如果要调整缩进的空格个数,只修改 shiftwidth 选项值即可。
按照 :help tabstop
命令的说明,设置 expandtab 选项后,可以修改 tabstop 选项值为 4,那么按 Tab 键插入 tab 字符后,也可以被替换成 4 个空格。
这里没有采用这个方案,而是选择配置 softtabstop 选项值,目的在于配置 softtabstop 选项值后,可以用 Backspace 键一次性删除 4 个空格,比较方便。而且配置 softtabstop 选项值为负数,可以自动保持跟 shiftwidth 选项值一致,便于修改。
可以查看 :help usr_30
的 Tabs and spaces 小节 和 :help usr_25
的 Indents and tabs 小节来获取更多的帮助信息。