├── README.md ├── doc └── detectindent.txt └── plugin └── detectindent.vim /README.md: -------------------------------------------------------------------------------- 1 | A Vim plugin, for automatically detecting indent settings. This plugin adds a 2 | :DetectIndent command, which tries to intelligently set the 'shiftwidth', 3 | 'expandtab' and 'tabstop' options based upon the existing settings in use in 4 | the active file. 5 | 6 | You may wish to consider one of the many forks of this project. Be aware that 7 | some of these forks have merged patches that I have been ignoring for good 8 | reasons, and that long lists of special cases are not necessarily a good 9 | idea... 10 | -------------------------------------------------------------------------------- /doc/detectindent.txt: -------------------------------------------------------------------------------- 1 | *detectindent.txt* The Detect Indent Plugin 1.0, Jan 04, 2005 2 | 3 | Author: Ciaran McCreesh 4 | 5 | ============================================================================== 6 | 1. Contents *detectindent* *detectindent-contents* 7 | 8 | 1. Contents |detectindent-contents| 9 | 2. :DetectIndent Command |:DetectIndent| 10 | Settings |detectindent-settings| 11 | 3. Uptime ChangeLog |uptime-changelog| 12 | 13 | ============================================================================== 14 | 2. :DetectIndent Command *:DetectIndent* 15 | 16 | The :DetectIndent command tries to intelligently set the 'shiftwidth', 17 | 'expandtab' and 'tabstop' options based upon the existing settings in 18 | use in the active file. 19 | 20 | Settings *detectindent-settings* 21 | 22 | When the correct value for 'expandtab' cannot be determined, it will 23 | usually retain its existing value. To specify that 'expandtab' should 24 | be used where autodetection is impossible, set: > 25 | :let g:detectindent_preferred_expandtab = 1 26 | < in your |vimrc| file. 27 | 28 | To set a preferred value for 'shiftwidth' and 'tabstop' when they 29 | cannot be automatically detected, set: > 30 | :let g:detectindent_preferred_indent = 4 31 | < in your |vimrc| file. 32 | 33 | To use the preferred values when both tabs and spaces are detected, 34 | set: > 35 | :let g:detectindent_preferred_when_mixed = 1 36 | < in your |vimrc| file. 37 | 38 | To set limit for number of lines that will be analysed set: > 39 | :let g:detectindent_max_lines_to_analyse = 1024 40 | < in your |vimrc| file. 41 | 42 | To override |detectindent_preferred_expandtab| for specific filetypes 43 | (example: use 4-character tabstops with tabs for python) set: > 44 | :let b:detectindent_preferred_expandtab = 0 45 | :let b:detectindent_preferred_indent = 4 46 | < after/ftplugin/FILETYPENAMEHERE.vim (example: 47 | after/ftplugin/python.vim). Using 0 acts as if the option was never 48 | set. 49 | 50 | ============================================================================== 51 | 3. DetectIndent ChangeLog *detectindent-changelog* 52 | 53 | v1.1 (20150225) 54 | * Add preferred_when_mixed. 55 | * Add buffer-local options. 56 | v1.0 (20050105) 57 | * initial release after discussion on irc.freenode.net:#vim 58 | 59 | ============================================================================== 60 | vim:tw=78:ts=8:ft=help 61 | -------------------------------------------------------------------------------- /plugin/detectindent.vim: -------------------------------------------------------------------------------- 1 | " Name: detectindent (global plugin) 2 | " Version: 1.0 3 | " Author: Ciaran McCreesh 4 | " Updates: http://github.com/ciaranm/detectindent 5 | " Purpose: Detect file indent settings 6 | " 7 | " License: You may redistribute this plugin under the same terms as Vim 8 | " itself. 9 | " 10 | " Usage: :DetectIndent 11 | " 12 | " " to prefer expandtab to noexpandtab when detection is 13 | " " impossible: 14 | " :let g:detectindent_preferred_expandtab = 1 15 | " 16 | " " to set a preferred indent level when detection is 17 | " " impossible: 18 | " :let g:detectindent_preferred_indent = 4 19 | " 20 | " " To use preferred values instead of guessing: 21 | " :let g:detectindent_preferred_when_mixed = 1 22 | " 23 | " Requirements: Untested on Vim versions below 6.2 24 | 25 | if exists("loaded_detectindent") 26 | finish 27 | endif 28 | let loaded_detectindent = 1 29 | 30 | if !exists('g:detectindent_verbosity') 31 | let g:detectindent_verbosity = 1 32 | endif 33 | 34 | fun! HasCStyleComments() 35 | return index(["c", "cpp", "java", "javascript", "php", "vala"], &ft) != -1 36 | endfun 37 | 38 | fun! IsCommentStart(line) 39 | " &comments aren't reliable 40 | return HasCStyleComments() && a:line =~ '/\*' 41 | endfun 42 | 43 | fun! IsCommentEnd(line) 44 | return HasCStyleComments() && a:line =~ '\*/' 45 | endfun 46 | 47 | fun! IsCommentLine(line) 48 | return HasCStyleComments() && a:line =~ '^\s\+//' 49 | endfun 50 | 51 | fun! s:GetValue(option) 52 | if exists('b:'. a:option) 53 | return get(b:, a:option) 54 | else 55 | return get(g:, a:option) 56 | endif 57 | endfun 58 | 59 | fun! DetectIndent() 60 | let l:has_leading_tabs = 0 61 | let l:has_leading_spaces = 0 62 | let l:shortest_leading_spaces_run = 0 63 | let l:shortest_leading_spaces_idx = 0 64 | let l:longest_leading_spaces_run = 0 65 | let l:max_lines = 1024 66 | if exists("g:detectindent_max_lines_to_analyse") 67 | let l:max_lines = g:detectindent_max_lines_to_analyse 68 | endif 69 | 70 | let verbose_msg = '' 71 | if ! exists("b:detectindent_cursettings") 72 | " remember initial values for comparison 73 | let b:detectindent_cursettings = {'expandtab': &et, 'shiftwidth': &sw, 'tabstop': &ts, 'softtabstop': &sts} 74 | endif 75 | 76 | let l:idx_end = line("$") 77 | let l:idx = 1 78 | while l:idx <= l:idx_end 79 | let l:line = getline(l:idx) 80 | 81 | " try to skip over comment blocks, they can give really screwy indent 82 | " settings in c/c++ files especially 83 | if IsCommentStart(l:line) 84 | while l:idx <= l:idx_end && ! IsCommentEnd(l:line) 85 | let l:idx = l:idx + 1 86 | let l:line = getline(l:idx) 87 | endwhile 88 | let l:idx = l:idx + 1 89 | continue 90 | endif 91 | 92 | " Skip comment lines since they are not dependable. 93 | if IsCommentLine(l:line) 94 | let l:idx = l:idx + 1 95 | continue 96 | endif 97 | 98 | " Skip lines that are solely whitespace, since they're less likely to 99 | " be properly constructed. 100 | if l:line !~ '\S' 101 | let l:idx = l:idx + 1 102 | continue 103 | endif 104 | 105 | let l:leading_char = strpart(l:line, 0, 1) 106 | 107 | if l:leading_char == "\t" 108 | let l:has_leading_tabs = 1 109 | 110 | elseif l:leading_char == " " 111 | " only interested if we don't have a run of spaces followed by a 112 | " tab. 113 | if -1 == match(l:line, '^ \+\t') 114 | let l:has_leading_spaces = 1 115 | let l:spaces = strlen(matchstr(l:line, '^ \+')) 116 | if l:shortest_leading_spaces_run == 0 || 117 | \ l:spaces < l:shortest_leading_spaces_run 118 | let l:shortest_leading_spaces_run = l:spaces 119 | let l:shortest_leading_spaces_idx = l:idx 120 | endif 121 | if l:spaces > l:longest_leading_spaces_run 122 | let l:longest_leading_spaces_run = l:spaces 123 | endif 124 | endif 125 | 126 | endif 127 | 128 | let l:idx = l:idx + 1 129 | 130 | let l:max_lines = l:max_lines - 1 131 | 132 | if l:max_lines == 0 133 | let l:idx = l:idx_end + 1 134 | endif 135 | 136 | endwhile 137 | 138 | if l:has_leading_tabs && ! l:has_leading_spaces 139 | " tabs only, no spaces 140 | let l:verbose_msg = "Detected tabs only and no spaces" 141 | setl noexpandtab 142 | if s:GetValue("detectindent_preferred_indent") 143 | let &l:shiftwidth = g:detectindent_preferred_indent 144 | let &l:tabstop = g:detectindent_preferred_indent 145 | endif 146 | 147 | elseif l:has_leading_spaces && ! l:has_leading_tabs 148 | " spaces only, no tabs 149 | let l:verbose_msg = "Detected spaces only and no tabs" 150 | setl expandtab 151 | let &l:shiftwidth = l:shortest_leading_spaces_run 152 | let &l:softtabstop = l:shortest_leading_spaces_run 153 | 154 | elseif l:has_leading_spaces && l:has_leading_tabs && ! s:GetValue("detectindent_preferred_when_mixed") 155 | " spaces and tabs 156 | let l:verbose_msg = "Detected spaces and tabs" 157 | setl noexpandtab 158 | let &l:shiftwidth = l:shortest_leading_spaces_run 159 | 160 | " mmmm, time to guess how big tabs are 161 | if l:longest_leading_spaces_run <= 2 162 | let &l:tabstop = 2 163 | elseif l:longest_leading_spaces_run <= 4 164 | let &l:tabstop = 4 165 | else 166 | let &l:tabstop = 8 167 | endif 168 | 169 | else 170 | " no spaces, no tabs 171 | let l:verbose_msg = s:GetValue("detectindent_preferred_when_mixed") ? "preferred_when_mixed is active" : "Detected no spaces and no tabs" 172 | if s:GetValue("detectindent_preferred_indent") && 173 | \ (s:GetValue("detectindent_preferred_expandtab")) 174 | setl expandtab 175 | let &l:shiftwidth = g:detectindent_preferred_indent 176 | let &l:softtabstop = g:detectindent_preferred_indent 177 | elseif s:GetValue("detectindent_preferred_indent") 178 | setl noexpandtab 179 | let &l:shiftwidth = g:detectindent_preferred_indent 180 | let &l:tabstop = g:detectindent_preferred_indent 181 | elseif s:GetValue("detectindent_preferred_expandtab") 182 | setl expandtab 183 | else 184 | setl noexpandtab 185 | endif 186 | 187 | endif 188 | 189 | if &verbose >= g:detectindent_verbosity 190 | echo l:verbose_msg 191 | \ ."; has_leading_tabs:" l:has_leading_tabs 192 | \ .", has_leading_spaces:" l:has_leading_spaces 193 | \ .", shortest_leading_spaces_run:" l:shortest_leading_spaces_run 194 | \ .", shortest_leading_spaces_idx:" l:shortest_leading_spaces_idx 195 | \ .", longest_leading_spaces_run:" l:longest_leading_spaces_run 196 | 197 | let changed_msg = [] 198 | for [setting, oldval] in items(b:detectindent_cursettings) 199 | exec 'let newval = &'.setting 200 | if oldval != newval 201 | let changed_msg += [ setting." changed from ".oldval." to ".newval ] 202 | end 203 | endfor 204 | if len(changed_msg) 205 | echo "Initial buffer settings changed:" join(changed_msg, ", ") 206 | endif 207 | endif 208 | endfun 209 | 210 | command! -bar -nargs=0 DetectIndent call DetectIndent() 211 | 212 | --------------------------------------------------------------------------------