3

Here is what I use for indentation:

set autoindent
set breakindent
set expandtab
set smarttab
set shiftround
set shiftwidth=2 softtabstop=-1  " or maybe shiftwidth=2 softtabstop=8

" https://vi.stackexchange.com/a/15557
inoremap <tab> <c-v><tab>

When I press >>, this indents my code 2 spaces; and when I press Tab, this inserts the tab character. Fine, isn't it?

However, there is a problem.

t------> // 1 tab

s....... // 7 spaces

Enter the Insert mode, put the cursor after the trailing tab or the trailing spaces, and press the Backspace key.

Here is how I would like it to work:

t // tab has been removed

s...... // 5 spaces

And here is how it actually works:

t..... // 5 spaces

s..... // 5 spaces

Of course, I can replace set shiftwidth=2 softtabstop=-1 with shiftwidth=2 softtabstop=8, but there will be another problem:

Instead of

t

s...... // 5 spaces

It will be

t

s

So my question:

How can I set up Vim so that...

  • When I press >>, it will indent code with 2 spaces
  • When I press Tab in Insert mode, it will insert the tab character
  • When I press Backspace and there are spaces before it, Backspace should remove 2 spaces per keypress
  • When I press Backspace and there are tabs before it, Backspace should remove 1 tab per keypress

2 Answers 2

2

As you described in your question, and as Matt confirmed in his answer, you cannot do this using only Vim's options.

We are, therefore, going to have to set up a mapping. There are several ways you could go about this, but here's how I'd do it:

set shiftwidth=2 softtabstop=0 noexpandtab
inoremap <expr> <BS> col('.') > 2 &&
  \ col('.') % 2 == 1 &&
  \ getline('.')[col('.') - 3:col('.') - 2] == '  '
  \ ? '<BS><BS>' : '<BS>'

This sets up an expression mapping so when you press Backspace it checks the text behind the cursor and backspaces twice if two spaces should be deleted.

N.B. You don't mention this in your requirements, but I made it only delete two spaces if the cursor is already at a multiple of 2. Because you have 'shiftround' set, this is how >> and << will work for you, so I thought you'd want Backspace to work in the same way. If you don't like this, you can delete the entire line \ col('.') % 2 == 1 &&.

2
  • Rich, thanks a lot for this mapping, but it seems there is an error. For example, if x is indented by 4 spaces, the Backspace moves it from the 5th column to 4th and then to the 2nd (whereas it is expected that it will move x from the 5th column to the 3rd and then to the 1st).
    – john c. j.
    Commented Aug 8, 2022 at 23:04
  • 1
    @johnc.j. Whoops! Sorry. Thanks for letting me know. The current version should now work correctly.
    – Rich
    Commented Aug 9, 2022 at 20:49
4
  1. When I press >>, it will indent code with 2 spaces
setlocal shiftwidth=2
  1. When I press Tab in Insert mode, it will insert the tab character
setlocal noexpandtab
  1. When I press Backspace and there are spaces before it, Backspace should remove 2 spaces per keypress
setlocal softtabstop=2
  1. When I press Backspace and there are tabs before it, Backspace should remove 1 tab per keypress
setlocal softtabstop=0

Obviously, 3 and 4 are incompatible with each other. You may also try setlocal shiftwidth=2 softtabstop=0 smarttab. Then at start of line backspace counts as "2 spaces" while in the middle as "1 space or tab".

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.