1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
" Numeric comparison function to sort in a Vim v7.0 compatible way
function! s:CompareNumeric(a, b) abort
return a:a > a:b ? 1 : -1
endfunction
" Entry point for plugin
function! detect_indent#() abort
" For spaces, we count both the total space-indented lines, and also the
" count of lines indexed by space count, so that if we need to, we can
" figure out a good 'shiftwidth' setting; for tabs, we just count the
" indented lines, since we won't need to set 'shiftwidth' for that.
"
let tabs = 0
let spaces = {
\ 'hist': {},
\ 'total': 0,
\}
" Figure out how many lines we'll check; cap this to 1,024, or whatever the
" user configured
let total = max([line('$'), get(g:, 'detect_indent_limit', 1024)])
" Iterate through the lines
for line in getline(1, total)
" If there are leading tabs, we'll call this a tab-indented line; bump the
" appropriate count, and skip the rest of the loop.
"
if matchstr(line, '^\t*') !=# ''
let tabs += 1
continue
endif
" Figure out count of space indenting; skip to the next line if it's zero
let indent = strlen(matchstr(line, '^ *'))
if indent == 0
continue
endif
" Increment the count of space-indented lines
let spaces['total'] += 1
" Create a dictionary entry in the histogram for this indent if necessary,
" and increment that counter
"
if !has_key(spaces['hist'], indent)
let spaces['hist'][indent] = 0
endif
let spaces['hist'][indent] += 1
endfor
" If the value for 'expandtab' as determined by the user's configuration
" matches the expected dominance of space-indented lines over tab-indented
" lines, we're already using the right setting, and we stop here
"
if &expandtab == (spaces['total'] >= tabs)
return
endif
" If 'expandtab' is set, we need to unset it and switch to pure tab
" indenting; that's straightforward, we just need to make sure 'shiftwidth'
" matches 'tabstop', and that 'softtabstop' is off.
"
if &expandtab
setlocal noexpandtab softtabstop=0
let &l:shiftwidth = &tabstop
" If 'expandtab' is not set, we need to set it, and guess an appropriate
" 'shiftwidth'.
else
setlocal expandtab
" Iterate through the keys of the histogram from smallest to largest.
" We'll accept as our 'shiftwidth' the smallest indent level that
" constitutes more than 5% of the total lines, configurable by the user.
" This is just an heuristic, but it seems to work pretty well.
"
let shiftwidth = 0
let indents = keys(spaces['hist'])
call map(indents, 'str2nr(v:val)') " Coerce the string keys to numeric
call sort(indents, 's:CompareNumeric') " Force numeric sort
for shiftwidth in indents
if spaces['hist'][shiftwidth] * 100 / spaces['total']
\ >= get(g:, 'detect_indent_confidence', 5)
break
endif
endfor
" We have our 'shiftwidth'; set it, with 'softtabstop' set to match
let &l:shiftwidth = shiftwidth
let &l:softtabstop = shiftwidth
endif
" Since we just messed with indent settings, stack up a command to revert
" the changes when the indent plugin is unloaded, as if we ourselves were
" a single filetype indent plugin.
"
let undo_indent = 'setlocal expandtab< shiftwidth< softtabstop<'
if exists('b:undo_indent')
let b:undo_indent .= '|' . undo_indent
else
let b:undo_indent = undo_indent
endif
endfunction
|