Skip to content

Improve space-based shiftwidth detection #16

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 49 additions & 47 deletions plugin/detectindent.vim
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@
" " impossible:
" :let g:detectindent_preferred_indent = 4
"
" Requirements: Untested on Vim versions below 6.2
" Requirements: Does not work on Vim versions below 7.0

" Bail out if we're on an old version of vim
if version < 700
finish
endif

if exists("loaded_detectindent")
finish
Expand All @@ -29,7 +34,7 @@ if !exists('g:detectindent_verbosity')
endif

fun! <SID>HasCStyleComments()
return index(["c", "cpp", "java", "javascript", "php"], &ft) != -1
return index(["c", "cpp", "h", "java", "javascript", "php"], &ft) != -1
endfun

fun! <SID>IsCommentStart(line)
Expand All @@ -45,12 +50,22 @@ fun! <SID>IsCommentLine(line)
return <SID>HasCStyleComments() && a:line =~ '^\s\+//'
endfun

fun! <SID>GCD(x, y)
let l:a = a:x
let l:b = a:y
while l:b > 0
let l:temp = l:b
let l:b = l:a % l:b
let l:a = l:temp
endwhile
return l:a
endfun

fun! <SID>DetectIndent()
let l:has_leading_tabs = 0
let l:has_leading_spaces = 0
let l:shortest_leading_spaces_run = 0
let l:shortest_leading_spaces_idx = 0
let l:longest_leading_spaces_run = 0
let l:leading_tab_count = 0
let l:leading_space_count = 0
let l:leading_space_dict = {}
let l:leading_spaces_gcd = 0
let l:max_lines = 1024
if exists("g:detectindent_max_lines_to_analyse")
let l:max_lines = g:detectindent_max_lines_to_analyse
Expand Down Expand Up @@ -94,22 +109,15 @@ fun! <SID>DetectIndent()
let l:leading_char = strpart(l:line, 0, 1)

if l:leading_char == "\t"
let l:has_leading_tabs = 1
let l:leading_tab_count = l:leading_tab_count + 1

elseif l:leading_char == " "
" only interested if we don't have a run of spaces followed by a
" tab.
if -1 == match(l:line, '^ \+\t')
let l:has_leading_spaces = 1
let l:leading_space_count = l:leading_space_count + 1
let l:spaces = strlen(matchstr(l:line, '^ \+'))
if l:shortest_leading_spaces_run == 0 ||
\ l:spaces < l:shortest_leading_spaces_run
let l:shortest_leading_spaces_run = l:spaces
let l:shortest_leading_spaces_idx = l:idx
endif
if l:spaces > l:longest_leading_spaces_run
let l:longest_leading_spaces_run = l:spaces
endif
let l:leading_space_dict[l:spaces] = get(l:leading_space_dict, l:spaces) + 1
endif

endif
Expand All @@ -124,40 +132,36 @@ fun! <SID>DetectIndent()

endwhile

if l:has_leading_tabs && ! l:has_leading_spaces
" tabs only, no spaces
let l:verbose_msg = "Detected tabs only and no spaces"
if l:leading_tab_count > l:leading_space_count
let l:verbose_msg = "Use tab to indent."
setl noexpandtab
if exists("g:detectindent_preferred_indent")
let &l:shiftwidth = g:detectindent_preferred_indent
let &l:tabstop = g:detectindent_preferred_indent
endif

elseif l:has_leading_spaces && ! l:has_leading_tabs
" spaces only, no tabs
let l:verbose_msg = "Detected spaces only and no tabs"
setl expandtab
let &l:shiftwidth = l:shortest_leading_spaces_run
let &l:softtabstop = l:shortest_leading_spaces_run

elseif l:has_leading_spaces && l:has_leading_tabs
" spaces and tabs
let l:verbose_msg = "Detected spaces and tabs"
setl noexpandtab
let &l:shiftwidth = l:shortest_leading_spaces_run
elseif l:leading_space_count > l:leading_tab_count
" Filter out those tab stops which occurred in < 10% of the lines
call filter(l:leading_space_dict, '100*v:val/l:leading_space_count >= 10')

" Find the greatest common divisor of the remaining tab stop lengths
let l:leading_spaces_gcd = 0
for length in keys(l:leading_space_dict)
if l:leading_spaces_gcd == 0
let l:leading_spaces_gcd = length
else
let l:leading_spaces_gcd = <SID>GCD(length, l:leading_spaces_gcd)
endif
endfor

" mmmm, time to guess how big tabs are
if l:longest_leading_spaces_run <= 2
let &l:tabstop = 2
elseif l:longest_leading_spaces_run <= 4
let &l:tabstop = 4
else
let &l:tabstop = 8
if l:leading_spaces_gcd != 0
let l:verbose_msg = "Use space to indent."
setl expandtab
let &l:shiftwidth = l:leading_spaces_gcd
let &l:softtabstop = l:leading_spaces_gcd
endif

else
" no spaces, no tabs
let l:verbose_msg = "Detected no spaces and no tabs"
let l:verbose_msg = "Cannot determine indent. Use default to indent."
if exists("g:detectindent_preferred_indent") &&
\ exists("g:detectindent_preferred_expandtab")
setl expandtab
Expand All @@ -177,11 +181,9 @@ fun! <SID>DetectIndent()

if &verbose >= g:detectindent_verbosity
echo l:verbose_msg
\ ."; has_leading_tabs:" l:has_leading_tabs
\ .", has_leading_spaces:" l:has_leading_spaces
\ .", shortest_leading_spaces_run:" l:shortest_leading_spaces_run
\ .", shortest_leading_spaces_idx:" l:shortest_leading_spaces_idx
\ .", longest_leading_spaces_run:" l:longest_leading_spaces_run
\ ."; leading_tab_count:" l:leading_tab_count
\ .", leading_space_count:" l:leading_space_count
\ .", leading_spaces_gcd:" l:leading_spaces_gcd

let changed_msg = []
for [setting, oldval] in items(b:detectindent_cursettings)
Expand Down