Tab Vs Vim 中的空间首选项

当涉及到选项卡和空间首选项时,Vim 非常通融。据我所知,tabstop设置表示制表符的宽度。shiftwidth设置指定在使用 <<>>命令时增加/减少多少列,而 softtabstop设置影响在插入模式下按下 Tab键时插入的空格数量。如果 expandtab打开,制表符键将插入 softtabstop个空格字符。关闭 expandtab时,按下 Tab键插入与 softtabstop匹配的选项卡 + 空格字符的数量尽可能少。(如果我说错了,请纠正我。)

最后一点让我想知道: 是否有一个实际的情况下,你不希望 shiftwidth == tabstop && tabstop == softtabstop?我想不出来。就我而言,如果我可以在一次赋值中将所有3个值设置为相同的值,那将是最方便的。例如:

:set stab=4

相当于跑步:

:set tabstop=4 softtabstop=4 shiftwidth=4

有人能告诉我们如何做到这一点吗?


更新

感谢迄今为止从 太多苯丙胺了HobbsKaiser的答复。我没有单独回答每个问题,而是在这里更新问题。

关闭 expandtab 的软停止

我在上面说过,关闭 expandtab 后,按 Tab 键会插入尽可能少的与 softtabstop匹配的 Tab + 空格字符。我同意,但我想我需要解释一下我的意思。我将试着举几个例子来说明这一点。若要继续,请运行 :set list,以便可以看到制表符。

tabstop=4 softtabstop=2 shiftwidth=4 noexpandtab

在插入模式下,按制表符键插入2个空格字符。第二次按制表符键,不再插入另外两个空格字符(总共4个空格字符) ,而是用一个制表符替换前两个空格。Tabstop 设置为4,因此单个制表符的宽度与4个空格相同。

tabstop=4 softtabstop=6 shiftwidth=4 noexpandtab

在插入模式下,按制表符键插入1个制表符加2个空格。制表符的宽度为4,因此总宽度为6,这是通过使用3个字符实现的。第二次按制表符键将插入两个制表符,并删除前面插入的两个空格。总宽度为12,这是用3个字符实现的。

在这两个示例中,Vim 都插入了匹配 softtabstop 的最小可能的 tab + 空格字符数。

如果我在关闭 expandtab 的情况下工作,我不会想要通过将 softtabstop 设置为与 tabstop 不同的值来实现额外的粒度控制。如果能够用一个命令将 tabstopsofttabstopshiftwidth设置为相同的值,对我来说仍然是有用的。

Expandtab 是否使软表格停止变得多余?

tabstop=4 softtabstop=0 shiftwidth=4 expandtab

在插入模式下,按制表符键插入4个空格。按删除键会删除一个空格——所以如果你不小心按到了 tab 键,你必须退后4次。

tabstop=4 softtabstop=4 shiftwidth=4 expandtab

在插入模式下,按制表符键插入4个空格。按退格键删除4个空格。

如果打开 expandtab,我希望删除键可以删除与选项卡键插入相同数量的空白。因此,在这种情况下,我也认为能够同时为 tabstopsofttabstopshiftwidth赋予相同的值是有用的。

走捷径还是有用的

Vim 提供如此多的灵活性,这很好,但我觉得自己不需要它。我只是想能够选择一个选项卡的宽度,以及它是一个“硬”选项卡(使用选项卡字符)还是一个“软”选项卡(由空格组成)。在硬选项卡和软选项卡之间切换很容易(:set expandtab!) ,但我希望它能更直接地设置选项卡的宽度,而不必摆弄3个不同的参数。

所以我提出的类似 :set stab=4的建议对我来说还是不错的。

25140 次浏览

Your understanding of softtabstop and expandtab is wrong - so the stab option you suggest wouldn't be very useful.

expandtab is for when you want to use spaces instead of tabs for everything. If you set expandtab, then Vim ignores the softtabstop option and uses tabstop and shiftwidth to work out how many spaces to insert.

softtabstop is only for when you would like to use a mix of tabs and spaces, allowing you to indent with fine control (2 or 4 spaces), while keeping tab width at a higher value (usually 8) so that text appears in the other applications. Setting softtabstop=tabstop doesn't accomplish anything because Vim will always use tabs for indenting.

Update: As kaizer.se has pointed out, if you are using expandtab, then you still need to set softtabstop if you want Vim to backspace multiple spaces as though they are a tab.

If expandtab is set then (as too much php points out), softtabstop becomes redundant. The only reason you might set shiftwidth differently from tabstop would be to cater to an odd habit; for instance, you use four-space indents but you prefer tab to insert eight spaces.

If expandtab is unset then things get fuzzier. If you want your code to look the same in with cat and non-vim editors as it does in vim, then tabstop should always be set at 8; in this case you would set softtabstop and shiftwidth both to your preferred indent level. If you instead prefer that every "physical tab" in the file represents one indent level, you would set tabstop and shiftwidth to your preferred indent level and leave softtabstop at zero (setting it equal to tabstop is equivalent except that if you change tabstop it will get out of sync, while zero just means "ignore this please").

This is my first attempt at writing VimScript, but here goes:

function! Stab(value)
let &shiftwidth  = a:value
let &softtabstop = a:value
let &tabstop     = a:value
endfunc

If I put this in my .vimrc file, I can call it by running :call Stab(X), where X is the desired tab width. This is an adequate solution for now, but if anyone can suggest a way of making it easier to call I would be grateful.

I've also created a function that quickly summarizes the current settings, which I have mapped to ctrl-Tab:

nmap <C-Tab> :call TabParams()<CR>
function! TabParams()
echo "tabstop:     ".&tabstop
echo "shiftwidth:  ".&shiftwidth
echo "softtabstop: ".&softtabstop
endfunc

Well, I put up a 100 point bounty for this answer, and now I've half solved it myself. Not sure if I can accept my own answer...

Are you changing your white space settings so often you really need a function to manage that? If you are messing with tabstop a lot and also setting expandtab, you are probably going to have a mess over time as you change files with different values passed to stab. Today I use :call stab (4), tomorrow it's :call stab (2) and last week it was :call stab (8). Sounds like even if you write it, you'll soon stop using it.

If you plan to always pass the same value to stab, why not just edit your global settings? In vim:

:e $MYVIMRC

and add the following:

set tabstop=4
set shiftwidth=4  "tabs are 4 spaces wide (default = 8)
set expandtab     "Convert tabs to spaces

This is how my .vimrc is setup.

You can in edit mode also use Ctrl-T to indent and Ctrl-D to deindent to the next indentation level as set by shiftwidth, regardless of the tabstop, softtabstop or expandtab settings. Vim will automatically add/remove spaces or tabs to bring you to the right column.

If you use these commands to control indentation instead of Tab/Backspace you don't have to worry about all these tab settings fitting together and always get to the correct indentation level.

Creating a stab option in Vim itself would not be easy, but I've whipped up this command/function that you can drop in your .vimrc (or a plugin file if you're super-organized). Use :Stab and you will be prompted for an indent level and whether or not to use expandtab. If you hit enter without giving it a new indent level, it will just print the current settings.

" put all this in your .vimrc or a plugin file
command! -nargs=* Stab call Stab()
function! Stab()
let l:tabstop = 1 * input('set shiftwidth=')


if l:tabstop > 0
" do we want expandtab as well?
let l:expandtab = confirm('set expandtab?', "&Yes\n&No\n&Cancel")
if l:expandtab == 3
" abort?
return
endif


let &l:sts = l:tabstop
let &l:ts = l:tabstop
let &l:sw = l:tabstop


if l:expandtab == 1
setlocal expandtab
else
setlocal noexpandtab
endif
endif


" show the selected options
try
echohl ModeMsg
echon 'set tabstop='
echohl Question
echon &l:ts
echohl ModeMsg
echon ' shiftwidth='
echohl Question
echon &l:sw
echohl ModeMsg
echon ' sts='
echohl Question
echon &l:sts . ' ' . (&l:et ? '  ' : 'no')
echohl ModeMsg
echon 'expandtab'
finally
echohl None
endtry
endfunction

One useful option is softtabstop=-1 which will set it to the value of shiftwidth.
You can also set shiftwidth to 0, in which case the tabstop value will be used.