commit 723c611178c8af808b29f491d6855db737963dcf Author: Tim Pope Date: Sun Dec 30 17:22:46 2012 -0500 sleuth.vim 1.0 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0a56e3f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/doc/tags diff --git a/README.markdown b/README.markdown new file mode 100644 index 0000000..d9e6340 --- /dev/null +++ b/README.markdown @@ -0,0 +1,53 @@ +# sleuth.vim + +This plugin automatically adjusts `'shiftwidth'` and `'tabstop'` heuristically +based on the current file, or, in the case the current file is new, blank, or +otherwise insufficient, by looking at other files of the same type in the +current and parent directories. In lieu of adjusting `'softtabstop'`, +`'smarttab'` is enabled. + +Compare to [DetectIndent][]. I wrote this because I wanted something fully +automatic. My goal is that by installing this plugin, you can remove all +indenting related configuration from your vimrc. + +[DetectIndent]: http://www.vim.org/scripts/script.php?script_id=1171 + +## Installation + +If you don't have a preferred installation method, I recommend +installing [pathogen.vim](https://github.com/tpope/vim-pathogen), and +then simply copy and paste: + + cd ~/.vim/bundle + git clone git://github.com/tpope/vim-sleuth.git + +## Notes + +* Searching for other files of the same type continues up the directory + hierarchy until a match is found. This means, for example, the indent for + the first file in a brand new Ruby project might very well be derived from + your `.irbrc`. I consider this a feature. +* The algorithm is rolled from scratch, fairly simplistic, and only lightly + battle tested. It's probably not (yet) as good as [DetectIndent][]. + Let me know what it fails on for you. + +Contributing +------------ + +See the contribution guidelines for +[pathogen.vim](https://github.com/tpope/vim-pathogen#readme). + +Self-Promotion +-------------- + +Like sleuth.vim? Follow the repository on +[GitHub](https://github.com/tpope/vim-sleuth). And if +you're feeling especially charitable, follow [tpope](http://tpo.pe/) on +[Twitter](http://twitter.com/tpope) and +[GitHub](https://github.com/tpope). + +License +------- + +Copyright (c) Tim Pope. Distributed under the same terms as Vim itself. +See `:help license`. diff --git a/doc/sleuth.txt b/doc/sleuth.txt new file mode 100644 index 0000000..6307c68 --- /dev/null +++ b/doc/sleuth.txt @@ -0,0 +1,17 @@ +*sleuth.txt* Heuristically set buffer options + +Author: Tim Pope +Repo: https://github.com/tpope/vim-sleuth +License: Same terms as Vim itself (see |license|) + +This plugin is only available if 'compatible' is not set. + +SUMMARY *sleuth* + +This plugin automatically adjusts 'shiftwidth' and 'tabstop' heuristically +based on the current file, or, in the case the current file is new, blank, or +otherwise insufficient, by looking at other files of the same type in the +current and parent directories. In lieu of adjusting 'softtabstop', +'smarttab' is enabled. + + vim:tw=78:et:ft=help:norl: diff --git a/plugin/sleuth.vim b/plugin/sleuth.vim new file mode 100644 index 0000000..c99329a --- /dev/null +++ b/plugin/sleuth.vim @@ -0,0 +1,125 @@ +" sleuth.vim - Heuristically set buffer options +" Maintainer: Tim Pope +" 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: