1

Under regular conditions, the tab key will insert a tab character and backspacing that tab deletes the whole tab character.

I have set up expandtab and softtabstop=4 so that when I hit tab it will insert 4 spaces and when I hit "ctrl-v-tab" it will insert a real tab. However, when I backspace real tabs, it will convert it to spaces and delete only 4 of those, leaving 4 behind (apparently backspace behavior is also affected by the softtabstop setup).

How can I set it up so that tab always deletes the whole tab character?

5
  • Is your tabstop set to 8? I believe your configuration is intended to do the exact opposite of what you want to do. If you :set tabstop=4 or :set softtabstop=8 it'd be a different story.
    – B Layer
    Jul 3, 2017 at 14:47
  • Indeed tabstop is set to 8; I still want to see tabs as occupying 8 spaces but I want to insert indents with 4 spaces. I just would prefer it not to change backspace behavior.
    – Nordico
    Jul 3, 2017 at 14:52
  • Right. But since the intended behavior of your configuration is to not allow the behavior you're seeking you'd probably have to do some key mapping or other personal customization to get there...which may be exactly what you're asking for.
    – B Layer
    Jul 3, 2017 at 14:55
  • If no one has an easier way you might build something based on this :inoremap <BS> <C-O>X
    – B Layer
    Jul 3, 2017 at 15:08
  • Not quite there because that seems to be deleting the character previous to the one I want to delete ('adf_' => 'af_'). I did think perhaps of mapping something like left arrow + supr to the backspace, which I'm not sure can be done and would be kind of dirty.
    – Nordico
    Jul 3, 2017 at 19:07

2 Answers 2

2

For kicks and because I'm trying to learn some Vim scripting here is a more elaborate solution.

This will handle backspace the way you expect, even if cursor is in first column (in which case it will join with the preceding line).

First add this to your vimrc:

" Sends equivalent of backspace key in Insert mode unless preceding
" character is a Tab in which case a Normal mode 'X' is sent. Allows
" backspace to completely delete Tab even if 'softtabstop' is set to
" a value less than the 'tabstop' setting
function! SmartBackSpace()
  if getline('.')[col('.') - 2] == "\<Tab>"
    normal! X
  else
    call feedkeys("\<C-H>")
  endif
endfunction

Then add this mapping:

inoremap <BS> <C-\><C-O>:call SmartBackSpace()<CR>

It's important that you use this because you need <C-\><C-O> instead of <C-O>. Otherwise the cursor may jump to undesirable places when transitioning from Insert mode to Normal mode.

8
  • 1. why the <C-\> in your mapping? 2. you don't need exe in the first conditional branch. 3. you can use normal! ~ instead of call feedkeys("^C ~").
    – Ryan Lue
    Jul 4, 2017 at 13:35
  • (To be clear, exe is useful when you want to use a variable in a command, like let insert = col('.') == 1 ? 'i' : 'a'; exec 'normal! ' . insert . '^H'.)
    – Ryan Lue
    Jul 4, 2017 at 13:39
  • 1
    Also possible, maybe simpler: inoremap <expr> <BS> getline('.')[col('.')-2] == '<Tab>' ? '<C-\><C-O>X' : '<BS>'
    – filbranden
    May 13, 2021 at 1:44
  • 1
    @filbranden That actually works perfectly! Thank you!
    – Sparhawk
    May 17, 2021 at 10:44
  • 1
    Thanks @BLayer. Done!
    – Sparhawk
    May 18, 2021 at 2:03
1

This mapping will take you out of Insert mode briefly and delete the previous character with 'X' rather than backspace before re-entering Insert mode:

inoremap <BS> <C-\><C-O>X

It seems to do the trick for most cases though there may be some edge cases not yet discovered. If there are too many issues I imagine mapping backspace to a simple function that looks at the previous character before deciding whether to use X or BS. That's the only way, for example, that you could retain the ability of backspace to "unwrap" or delete from the beginning of a line to end of the previous line and such which folks often enable in their vimrc files with lines like set backspace=eol,start,indent

Note: I first suggested something like this mapping in a comment but I hadn't accounted for the cursor movement wonkiness when entering Normal mode (in the middle of the line behaves differently from the end of the line). The Ctrl-\ should take care of it.

2
  • Sorry to be such a nuisance, this almost gets it but the only problem is that now I cannot delete newline characters with backspace. (Note: also, I did see your other reply, but I would like to configure this only using vim customization without scripting). EDIT: now I see that you mention exactly this; are you sure there is no configurable way to do this?
    – Nordico
    Jul 4, 2017 at 16:05
  • I don't know how it can be done without conditional logic of some kind. It may be possible to inline the function script in the key mapping.
    – B Layer
    Jul 4, 2017 at 22:42

Your Answer

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

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