Permalink
Browse files

Add sleuth.vim

  • Loading branch information...
1 parent db76d86 commit d266162d75997d3e0c5002029abf0339baa6eb4d @tpope committed Dec 30, 2012
Showing with 205 additions and 60 deletions.
  1. +125 −0 .vim/plugin/sleuth.vim
  2. +80 −60 .vimrc
View
@@ -0,0 +1,125 @@
+" sleuth.vim - Heuristically set buffer options
+" Maintainer: Tim Pope <http://tpo.pe/>
+" Version: 1.0
+
+if exists("g:loaded_sleuth") || v:version < 700 || &cp
+ finish
+endif
+let g:loaded_sleuth = 1
+
+function! s:guess(lines) abort
+ let options = {}
+ let ccomment = 0
+
+ for line in a:lines
+
+ if line =~# '^\s\+$'
+ continue
+ endif
+
+ if line =~# '^\s*/\*'
+ let ccomment = 1
+ endif
+ if ccomment
+ if line =~# '\*/'
+ let ccomment = 0
+ endif
+ continue
+ endif
+
+ let softtab = repeat(' ', 8)
+ if line =~# '^\t'
+ let options.expandtab = 0
+ elseif line =~# '^' . softtab
+ let options.expandtab = 1
+ endif
+ let indent = len(matchstr(substitute(line, '\t', softtab, 'g'), '^ *'))
+ if indent > 1 && get(options, 'shiftwidth', 99) > indent
+ let options.shiftwidth = indent
+ endif
+
+ endfor
+
+ 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'],
+ \ }
+ 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
+ if &verbose
+ echomsg string(a:options)
+ endif
+ return 1
+ endif
+endfunction
+
+function! s:detect() abort
+ let options = s:guess(getline(1, 1024))
+ if s:apply_if_ready(options)
+ return
+ endif
+ let patterns = s:patterns_for(&filetype)
+ call filter(patterns, 'v:val !~# "/"')
+ if !empty(patterns)
+ let pattern = len(patterns) == 1 ? patterns[0] : '{'.join(patterns, ',').'}'
+ let dir = expand('%:p:h')
+ while isdirectory(dir) && dir !=# fnamemodify(dir, ':h')
+ for neighbor in split(glob(dir.'/'.pattern), "\n")
+ if neighbor !=# expand('%:p')
+ call extend(options, s:guess(readfile(neighbor, '', 1024)), 'keep')
+ endif
+ if s:apply_if_ready(options)
+ return
+ endif
+ endfor
+ let dir = fnamemodify(dir, ':h')
+ endwhile
+ endif
+ 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
+
+augroup sleuth
+ autocmd!
+ autocmd FileType * call s:detect()
+augroup END
+
+" vim:set et sw=2:
Oops, something went wrong.

0 comments on commit d266162

Please sign in to comment.