diff options
Diffstat (limited to 'opt/sleuth/plugin')
-rw-r--r-- | opt/sleuth/plugin/sleuth.vim | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/opt/sleuth/plugin/sleuth.vim b/opt/sleuth/plugin/sleuth.vim new file mode 100644 index 0000000..e5f6355 --- /dev/null +++ b/opt/sleuth/plugin/sleuth.vim @@ -0,0 +1,206 @@ +" sleuth.vim - Heuristically set buffer options +" Maintainer: Tim Pope <http://tpo.pe/> +" Version: 1.1 +" GetLatestVimScripts: 4375 1 :AutoInstall: sleuth.vim + +if exists("g:loaded_sleuth") || v:version < 700 || &cp + finish +endif +let g:loaded_sleuth = 1 + +function! s:guess(lines) abort + let options = {} + let heuristics = {'spaces': 0, 'hard': 0, 'soft': 0} + let ccomment = 0 + let podcomment = 0 + let triplequote = 0 + let backtick = 0 + let xmlcomment = 0 + let softtab = repeat(' ', 8) + + for line in a:lines + if !len(line) || line =~# '^\s*$' + continue + endif + + if line =~# '^\s*/\*' + let ccomment = 1 + endif + if ccomment + if line =~# '\*/' + let ccomment = 0 + endif + continue + endif + + if line =~# '^=\w' + let podcomment = 1 + endif + if podcomment + if line =~# '^=\%(end\|cut\)\>' + let podcomment = 0 + endif + continue + endif + + if triplequote + if line =~# '^[^"]*"""[^"]*$' + let triplequote = 0 + endif + continue + elseif line =~# '^[^"]*"""[^"]*$' + let triplequote = 1 + endif + + if backtick + if line =~# '^[^`]*`[^`]*$' + let backtick = 0 + endif + continue + elseif line =~# '^[^`]*`[^`]*$' + let backtick = 1 + endif + + if line =~# '^\s*<\!--' + let xmlcomment = 1 + endif + if xmlcomment + if line =~# '-->' + let xmlcomment = 0 + endif + continue + endif + + if line =~# '^\t' + let heuristics.hard += 1 + elseif line =~# '^' . softtab + let heuristics.soft += 1 + endif + if line =~# '^ ' + let heuristics.spaces += 1 + endif + let indent = len(matchstr(substitute(line, '\t', softtab, 'g'), '^ *')) + if indent > 1 && (indent < 4 || indent % 2 == 0) && + \ get(options, 'shiftwidth', 99) > indent + let options.shiftwidth = indent + endif + endfor + + if heuristics.hard && !heuristics.spaces + return {'expandtab': 0, 'shiftwidth': &tabstop} + elseif heuristics.soft != heuristics.hard + let options.expandtab = heuristics.soft > heuristics.hard + if heuristics.hard + let options.tabstop = 8 + endif + endif + + return options +endfunction + +function! s:patterns_for(type) abort + if a:type ==# '' + return [] + endif + if !exists('s:patterns') + redir => capture + silent autocmd BufRead + redir END + let patterns = { + \ 'c': ['*.c'], + \ 'html': ['*.html'], + \ 'sh': ['*.sh'], + \ 'vim': ['vimrc', '.vimrc', '_vimrc'], + \ } + let setfpattern = '\s\+\%(setf\%[iletype]\s\+\|set\%[local]\s\+\%(ft\|filetype\)=\|call SetFileTypeSH(["'']\%(ba\|k\)\=\%(sh\)\@=\)' + for line in split(capture, "\n") + let match = matchlist(line, '^\s*\(\S\+\)\='.setfpattern.'\(\w\+\)') + if !empty(match) + call extend(patterns, {match[2]: []}, 'keep') + call extend(patterns[match[2]], [match[1] ==# '' ? last : match[1]]) + endif + let last = matchstr(line, '\S.*') + endfor + let s:patterns = patterns + endif + return copy(get(s:patterns, a:type, [])) +endfunction + +function! s:apply_if_ready(options) abort + if !has_key(a:options, 'expandtab') || !has_key(a:options, 'shiftwidth') + return 0 + else + for [option, value] in items(a:options) + call setbufvar('', '&'.option, value) + endfor + return 1 + endif +endfunction + +function! s:detect() abort + if &buftype ==# 'help' + return + endif + + let options = s:guess(getline(1, 1024)) + if s:apply_if_ready(options) + return + endif + let c = get(b:, 'sleuth_neighbor_limit', get(g:, 'sleuth_neighbor_limit', 20)) + let patterns = c > 0 ? s:patterns_for(&filetype) : [] + call filter(patterns, 'v:val !~# "/"') + let dir = expand('%:p:h') + while isdirectory(dir) && dir !=# fnamemodify(dir, ':h') && c > 0 + for pattern in patterns + for neighbor in split(glob(dir.'/'.pattern), "\n")[0:7] + if neighbor !=# expand('%:p') && filereadable(neighbor) + call extend(options, s:guess(readfile(neighbor, '', 256)), 'keep') + let c -= 1 + endif + if s:apply_if_ready(options) + let b:sleuth_culprit = neighbor + return + endif + if c <= 0 + break + endif + endfor + if c <= 0 + break + endif + endfor + let dir = fnamemodify(dir, ':h') + endwhile + if has_key(options, 'shiftwidth') + return s:apply_if_ready(extend({'expandtab': 1}, options)) + endif +endfunction + +setglobal smarttab + +if !exists('g:did_indent_on') + filetype indent on +endif + +function! SleuthIndicator() abort + let sw = &shiftwidth ? &shiftwidth : &tabstop + if &expandtab + return 'sw='.sw + elseif &tabstop == sw + return 'ts='.&tabstop + else + return 'sw='.sw.',ts='.&tabstop + endif +endfunction + +augroup sleuth + autocmd! + autocmd FileType * + \ if get(b:, 'sleuth_automatic', get(g:, 'sleuth_automatic', 1)) + \ | call s:detect() | endif + autocmd User Flags call Hoist('buffer', 5, 'SleuthIndicator') +augroup END + +command! -bar -bang Sleuth call s:detect() + +" vim:set et sw=2: |