Yet Another Detect Indent. Here is how it works in my .vimrc
:
" Try to auto detect and use the indentation of a file when opened.
autocmd BufRead * DetectIndent
" Otherwise use file type specific indentation. E.g. tabs for Makefiles
" and 4 spaces for Python. This is optional.
filetype plugin indent on
" Set a fallback here in case detection fails and there is no file type
" plugin available. You can also omit this, then Vim defaults to tabs.
set expandtab shiftwidth=4 softtabstop=4
" You stay in control of your tabstop setting.
set tabstop=4
The plugin is built on the following principles:
- Ignore outliers. A few bad whitespace should not stop the plugin from working. But as a user I don't want to be warned of or worse required to fix those inconsistencies. That's not in the scope of this plugin.
- Leave settings unchanged if detection fails. The users
.vimrc
or file type plugins should then determine the indentation style. - Only ever touch the
3 settings relevant to indentation:
expandtab
,shiftwidth
andsofttabstop
- Be a single, short and easy to comprehend Vimscript file.
Not convinced yet? Take a look at the code, it fits on a single page.
The :DetectIndent
command tries to auto detect the indentation style in the
current buffer.
If it finds tabs it sets noexpandtab shiftwidth=0 softtabstop=0
,
if it finds n spaces it sets expandtab shiftwidth=n softtabstop=n
.
If the algorithm can't confidently determine the indentation style no settings
are changed. It comes naturally to set this as an autocommand as can be seen
above.
The :IndentTabs
and :IndentSpaces <n>
commands explicitly apply the
settings for tabs and spaces respectively. The argument to :IndentSpaces
can
be omitted in which case your tabstop setting will be used.
The developers of the Firefox dev tools wrote a great article where they compare different indentation detection strategies for Firefox's built-in source editor. yadi.vim uses the "comparing lines" strategy from the article because it performs well and is easy to implement. The article explains it well:
This method compares the indentation of each line with the previous line, and adds the difference to a tally. So if a line is indented by 10 spaces, and the previous by 8, one more vote would be added for 2-space indentation.
Mixed tabs and spaces as well as 1-space indentation are not supported. This is a deliberate choice as these indentation styles are rare and would complicate the algorithm considerably.
Furthermore there is a heuristic in place to prevent misdetection. More than 80% of the file must be either tabs or spaces for the detection to succeed. Additionally in the case of spaces the most common indentation level must make up more than 60% of all indentations.
To display the current indentation settings in your statusline you could use this expression:
let &statusline = '%{&expandtab?shiftwidth()." sp":"tabs"}'
Or with lightline.vim:
let g:lightline = {
\ 'active': {
\ 'right': [ [ 'lineinfo' ],
\ [ 'percent' ],
\ [ 'fileformat', 'fileencoding', 'filetype', 'indentstyle' ] ]
\ },
\ 'component': {
\ 'indentstyle': '%{&expandtab?shiftwidth()." sp":"tabs"}'
\ },
\ }
There are a bunch of plugins with DetectIndent-like functionality:
But none of them let you fall back to file type plugins if detection fails. Most of them force you to set a fallback indentation style. sleuth.vim is more sophisticated and tries to guess the indentation from surrounding files. It's a different approach which comes at the cost of greater complexity and makes the result rather unpredictable.