From 4be4ad2c865cb6208d1b2b5eb955540e707145e2 Mon Sep 17 00:00:00 2001 From: Hugues Hiegel Date: Tue, 16 Feb 2016 11:17:36 +0100 Subject: [tabs] try to detect indentation at each buffer... (well.. it tries..) --- doc/detectindent.txt | 60 ++++++++++++++ plugin/detectindent.vim | 211 ++++++++++++++++++++++++++++++++++++++++++++++++ vimrc | 11 ++- 3 files changed, 281 insertions(+), 1 deletion(-) create mode 100644 doc/detectindent.txt create mode 100644 plugin/detectindent.vim diff --git a/doc/detectindent.txt b/doc/detectindent.txt new file mode 100644 index 0000000..524b8b6 --- /dev/null +++ b/doc/detectindent.txt @@ -0,0 +1,60 @@ +*detectindent.txt* The Detect Indent Plugin 1.0, Jan 04, 2005 + +Author: Ciaran McCreesh + +============================================================================== +1. Contents *detectindent* *detectindent-contents* + + 1. Contents |detectindent-contents| + 2. :DetectIndent Command |:DetectIndent| + Settings |detectindent-settings| + 3. Uptime ChangeLog |uptime-changelog| + +============================================================================== +2. :DetectIndent Command *:DetectIndent* + + The :DetectIndent command tries to intelligently set the 'shiftwidth', + 'expandtab' and 'tabstop' options based upon the existing settings in + use in the active file. + + Settings *detectindent-settings* + + When the correct value for 'expandtab' cannot be determined, it will + usually retain its existing value. To specify that 'expandtab' should + be used where autodetection is impossible, set: > + :let g:detectindent_preferred_expandtab = 1 +< in your |vimrc| file. + + To set a preferred value for 'shiftwidth' and 'tabstop' when they + cannot be automatically detected, set: > + :let g:detectindent_preferred_indent = 4 +< in your |vimrc| file. + + To use the preferred values when both tabs and spaces are detected, + set: > + :let g:detectindent_preferred_when_mixed = 1 +< in your |vimrc| file. + + To set limit for number of lines that will be analysed set: > + :let g:detectindent_max_lines_to_analyse = 1024 +< in your |vimrc| file. + + To override |detectindent_preferred_expandtab| for specific filetypes + (example: use 4-character tabstops with tabs for python) set: > + :let b:detectindent_preferred_expandtab = 0 + :let b:detectindent_preferred_indent = 4 +< after/ftplugin/FILETYPENAMEHERE.vim (example: + after/ftplugin/python.vim). Using 0 acts as if the option was never + set. + +============================================================================== +3. DetectIndent ChangeLog *detectindent-changelog* + + v1.1 (20150225) + * Add preferred_when_mixed. + * Add buffer-local options. + v1.0 (20050105) + * initial release after discussion on irc.freenode.net:#vim + +============================================================================== +vim:tw=78:ts=8:ft=help diff --git a/plugin/detectindent.vim b/plugin/detectindent.vim new file mode 100644 index 0000000..d903948 --- /dev/null +++ b/plugin/detectindent.vim @@ -0,0 +1,211 @@ +" Name: detectindent (global plugin) +" Version: 1.0 +" Author: Ciaran McCreesh +" Updates: http://github.com/ciaranm/detectindent +" Purpose: Detect file indent settings +" +" License: You may redistribute this plugin under the same terms as Vim +" itself. +" +" Usage: :DetectIndent +" +" " to prefer expandtab to noexpandtab when detection is +" " impossible: +" :let g:detectindent_preferred_expandtab = 1 +" +" " to set a preferred indent level when detection is +" " impossible: +" :let g:detectindent_preferred_indent = 4 +" +" " To use preferred values instead of guessing: +" :let g:detectindent_preferred_when_mixed = 1 +" +" Requirements: Untested on Vim versions below 6.2 + +if exists("loaded_detectindent") + finish +endif +let loaded_detectindent = 1 + +if !exists('g:detectindent_verbosity') + let g:detectindent_verbosity = 1 +endif + +fun! HasCStyleComments() + return index(["c", "cpp", "java", "javascript", "php", "vala"], &ft) != -1 +endfun + +fun! IsCommentStart(line) + " &comments aren't reliable + return HasCStyleComments() && a:line =~ '/\*' +endfun + +fun! IsCommentEnd(line) + return HasCStyleComments() && a:line =~ '\*/' +endfun + +fun! IsCommentLine(line) + return HasCStyleComments() && a:line =~ '^\s\+//' +endfun + +fun! s:GetValue(option) + if exists('b:'. a:option) + return get(b:, a:option) + else + return get(g:, a:option) + endif +endfun + +fun! 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:max_lines = 1024 + if exists("g:detectindent_max_lines_to_analyse") + let l:max_lines = g:detectindent_max_lines_to_analyse + endif + + let verbose_msg = '' + if ! exists("b:detectindent_cursettings") + " remember initial values for comparison + let b:detectindent_cursettings = {'expandtab': &et, 'shiftwidth': &sw, 'tabstop': &ts, 'softtabstop': &sts} + endif + + let l:idx_end = line("$") + let l:idx = 1 + while l:idx <= l:idx_end + let l:line = getline(l:idx) + + " try to skip over comment blocks, they can give really screwy indent + " settings in c/c++ files especially + if IsCommentStart(l:line) + while l:idx <= l:idx_end && ! IsCommentEnd(l:line) + let l:idx = l:idx + 1 + let l:line = getline(l:idx) + endwhile + let l:idx = l:idx + 1 + continue + endif + + " Skip comment lines since they are not dependable. + if IsCommentLine(l:line) + let l:idx = l:idx + 1 + continue + endif + + " Skip lines that are solely whitespace, since they're less likely to + " be properly constructed. + if l:line !~ '\S' + let l:idx = l:idx + 1 + continue + endif + + let l:leading_char = strpart(l:line, 0, 1) + + if l:leading_char == "\t" + let l:has_leading_tabs = 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: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 + endif + + endif + + let l:idx = l:idx + 1 + + let l:max_lines = l:max_lines - 1 + + if l:max_lines == 0 + let l:idx = l:idx_end + 1 + endif + + endwhile + + if l:has_leading_tabs && ! l:has_leading_spaces + " tabs only, no spaces + let l:verbose_msg = "Detected tabs only and no spaces" + setl noexpandtab + if s:GetValue("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 && ! s:GetValue("detectindent_preferred_when_mixed") + " spaces and tabs + let l:verbose_msg = "Detected spaces and tabs" + setl noexpandtab + let &l:shiftwidth = l:shortest_leading_spaces_run + + " 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 + endif + + else + " no spaces, no tabs + let l:verbose_msg = s:GetValue("detectindent_preferred_when_mixed") ? "preferred_when_mixed is active" : "Detected no spaces and no tabs" + if s:GetValue("detectindent_preferred_indent") && + \ (s:GetValue("detectindent_preferred_expandtab")) + setl expandtab + let &l:shiftwidth = g:detectindent_preferred_indent + let &l:softtabstop = g:detectindent_preferred_indent + elseif s:GetValue("detectindent_preferred_indent") + setl noexpandtab + let &l:shiftwidth = g:detectindent_preferred_indent + let &l:tabstop = g:detectindent_preferred_indent + elseif s:GetValue("detectindent_preferred_expandtab") + setl expandtab + else + setl noexpandtab + endif + + endif + + 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 + + let changed_msg = [] + for [setting, oldval] in items(b:detectindent_cursettings) + exec 'let newval = &'.setting + if oldval != newval + let changed_msg += [ setting." changed from ".oldval." to ".newval ] + end + endfor + if len(changed_msg) + echo "Initial buffer settings changed:" join(changed_msg, ", ") + endif + endif +endfun + +command! -bar -nargs=0 DetectIndent call DetectIndent() + diff --git a/vimrc b/vimrc index 1885ee1..500f6f8 100644 --- a/vimrc +++ b/vimrc @@ -1,4 +1,8 @@ +filetype plugin indent on +" To ignore plugin indent changes, instead use: +"filetype plugin on + color default "syntax clear @@ -265,7 +269,12 @@ set mousem=extend set tabstop=4 set shiftwidth=4 -set noexpandtab +set expandtab +let g:detectindent_preferred_expandtab = 1 +let g:detectindent_preferred_indent = 4 +augroup DetectIndentation + autocmd BufRead * DetectIndent +augroup END " Draws underline (waits for char input) noremap U yyp0$r -- cgit v1.2.3