vim - set auto indent to fill the leading space with space or tabstop
-
28-10-2019 - |
Question
It seems if we enable 'ai', vim will fill the the leading space with tabstop. I can make it fill with just space with 'et'. I don't like a C file mixed with space and tabstop.
My vimrc:
set ts=4 et
set ai
set hlsearch
syntax on
filetype plugin indent on
autocmd FileType make setlocal noexpandtab
However, in some condition I do need to input tabstop when I hit the 'TAB' on keyboard, for example, in makefile and some others. The 'autocmd FileType' command is not good: I can't add every file type in vimrc.
What I want is simple:
- autoindent to fill leading area with space;
- when hit 'TAB' on keyboard, tabstop input, not space (so no 'et')
How to do it?
Solution
inoremap <expr> <tab> ((getline('.')[:col('.')-2]=~'\S')?("\<C-v>\t"):(repeat(' ', &ts-((virtcol('.')-1)%&ts))))
It does the same as @Lynch answer if I read it correctly.
You can also use <C-v><Tab>
: this will insert <Tab>
without invoking any mappings and ignores expandtab unless you remapped <C-v>
or <C-v><Tab>
for some reason.
If you want to just insert tab do
inoremap <Tab> <C-v><Tab>
It will ignore expandtab setting.
OTHER TIPS
I did it using a function. I tested it, but maybe in some particular case you will have to fix some bugs. Try adding this to your vimrc:
set et
function! Inserttab()
let insert = ""
let line = getline('.')
let pos = getpos('.')[2]
let before = ""
let after = line
if pos != 1
let before = line[ 0: pos - 1]
let after = line[pos : strlen(line) ]
endif
if pos != 1 && substitute(before, "[ \t]", "", "g") != ""
let insert = "\t"
else
let insert = " "
endif
let line = before . insert . after
call setline('.', line)
call cursor(line('.'), strlen(before . insert))
endfunction
inoremap <tab> <esc>:call Inserttab()<CR>a
Basicaly it does remap your key in visual mode to the function Inserttab(). Also note that if you change ts for something other than 4 it will still output 4 spaces instead of two because the value is hard coded.
Also im not very familiar with vim scripts, but I think all the variables used will be global which is a bad thing.
I forgot to mention that to "see" white spaces you can use set list
. You disable this with set nolist
. Also in normal mode you can use ga
to see information about the character your cursor is on.
Edit I realise that you may want to insert tab at the beginin of the line. My script insert space at the begining and tab anywhere else.
If you really want a tab every time you hit tab key you could simply use this:
set et
function! Inserttab()
let insert = ""
let line = getline('.')
let pos = getpos('.')[2]
let before = ""
let after = line
if pos != 1
let before = line[ 0: pos - 1]
let after = line[pos : strlen(line) ]
endif
let insert = "\t"
let line = before . insert . after
call setline('.', line)
call cursor(line('.'), strlen(before . insert))
endfunction
inoremap <tab> <esc>:call Inserttab()<CR>a
But I dont see the point, with this version you will never be able to indent manually from insert mode.
One way to do it is
- :set sw=4 (or whatever you want)
- :set ts=46 (or some large number)
Then autoindent will not insert tabs unless you reach 46 spaces, in which case you can put in a higher number.
Only drag about this is if someone else is using tabs, then you have to reset ts to agree with the file you are editing. On the other hand, it will make the tabs immediately obvious, which can be desirable as well.