From 4b364f096f9c5ad06daa99f5d302e0b07424f0bf Mon Sep 17 00:00:00 2001 From: Tom Ryder Date: Sat, 30 May 2020 19:21:03 +1200 Subject: First version, spun out from dotfiles v9.10.0 --- README.md | 0 VERSION | 1 + autoload/detect_indent.vim | 108 +++++++++++++++++++++++++++++++++++++++++++++ doc/detect_indent.txt | 48 ++++++++++++++++++++ plugin/detect_indent.vim | 23 ++++++++++ 5 files changed, 180 insertions(+) create mode 100644 README.md create mode 100644 VERSION create mode 100644 autoload/detect_indent.vim create mode 100644 doc/detect_indent.txt create mode 100644 plugin/detect_indent.vim diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..6e8bf73 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.1.0 diff --git a/autoload/detect_indent.vim b/autoload/detect_indent.vim new file mode 100644 index 0000000..71a43e8 --- /dev/null +++ b/autoload/detect_indent.vim @@ -0,0 +1,108 @@ +" Numeric comparison function to sort in a Vim v7.0 compatible way +function! s:CompareNumeric(a, b) abort + return a:a > a:b ? 1 : -1 +endfunction + +" Entry point for plugin +function! detect_indent#() abort + + " For spaces, we count both the total space-indented lines, and also the + " count of lines indexed by space count, so that if we need to, we can + " figure out a good 'shiftwidth' setting; for tabs, we just count the + " indented lines, since we won't need to set 'shiftwidth' for that. + " + let tabs = 0 + let spaces = { + \ 'hist': {}, + \ 'total': 0, + \} + + " Figure out how many lines we'll check; cap this to 1,024, or whatever the + " user configured + let total = max([line('$'), get(g:, 'detect_indent_limit', 1024)]) + + " Iterate through the lines + for line in getline(1, total) + + " If there are leading tabs, we'll call this a tab-indented line; bump the + " appropriate count, and skip the rest of the loop. + " + if matchstr(line, '^\t*') !=# '' + let tabs += 1 + continue + endif + + " Figure out count of space indenting; skip to the next line if it's zero + let indent = strlen(matchstr(line, '^ *')) + if indent == 0 + continue + endif + + " Increment the count of space-indented lines + let spaces['total'] += 1 + + " Create a dictionary entry in the histogram for this indent if necessary, + " and increment that counter + " + if !has_key(spaces['hist'], indent) + let spaces['hist'][indent] = 0 + endif + let spaces['hist'][indent] += 1 + + endfor + + " If the value for 'expandtab' as determined by the user's configuration + " matches the expected dominance of space-indented lines over tab-indented + " lines, we're already using the right setting, and we stop here + " + if &expandtab == (spaces['total'] >= tabs) + return + endif + + " If 'expandtab' is set, we need to unset it and switch to pure tab + " indenting; that's straightforward, we just need to make sure 'shiftwidth' + " matches 'tabstop', and that 'softtabstop' is off. + " + if &expandtab + setlocal noexpandtab softtabstop=0 + let &l:shiftwidth = &tabstop + + " If 'expandtab' is not set, we need to set it, and guess an appropriate + " 'shiftwidth'. + else + setlocal expandtab + + " Iterate through the keys of the histogram from smallest to largest. + " We'll accept as our 'shiftwidth' the smallest indent level that + " constitutes more than 5% of the total lines, configurable by the user. + " This is just an heuristic, but it seems to work pretty well. + " + let shiftwidth = 0 + let indents = keys(spaces['hist']) + call map(indents, 'str2nr(v:val)') " Coerce the string keys to numeric + call sort(indents, 's:CompareNumeric') " Force numeric sort + for shiftwidth in indents + if spaces['hist'][shiftwidth] * 100 / spaces['total'] + \ >= get(g:, 'detect_indent_confidence', 5) + break + endif + endfor + + " We have our 'shiftwidth'; set it, with 'softtabstop' set to match + let &l:shiftwidth = shiftwidth + let &l:softtabstop = shiftwidth + + endif + + " Since we just messed with indent settings, stack up a command to revert + " the changes when the indent plugin is unloaded, as if we ourselves were + " a single filetype indent plugin. + " + let undo_indent = 'setlocal expandtab< shiftwidth< softtabstop<' + if exists('b:undo_indent') + let b:undo_indent .= '|' . undo_indent + else + let b:undo_indent = undo_indent + endif + +endfunction diff --git a/doc/detect_indent.txt b/doc/detect_indent.txt new file mode 100644 index 0000000..7ba0fa8 --- /dev/null +++ b/doc/detect_indent.txt @@ -0,0 +1,48 @@ +*detect_indent.txt* For Vim version 7.0 Last change: 2020 May 30 + +DESCRIPTION *detect_indent* + +This plugin provides a user command to be run on a buffer after filetype +detection, applying some simple heuristics to adjust indentation settings if +the user's settings don't appear to match the buffer's settings. It pivots on +the value of 'expandtab'. + +If changing from 'noexpandtab' to 'expandtab', a guess at an appropriate value +for 'shiftround' is attempted. It may not be accurate. + +REQUIREMENTS *detect_indent-requirements* + +This plugin is only available if 'compatible' is not set. It also requires +the |+user_commands| feature. + +COMMANDS *detect_indent-commands* + + *:DetectIndent* +Run the plugin's detection manually if needed. You will probably only want to +do this for debugging purposes, as the command should be run after |FileType| +hooks. + +OPTIONS *detect_indent-options* + + *g:detect_indent_limit* +Set `g:detect_indent_limit` to the upper limit on the number of lines checked +to detect the indent. The default value is 1024. + + *g:detect_indent_confidence* +Set `g:detect_indent_confidence` to the minimum percentage of space indent +levels of the total indented lines sufficient to adopt that value for +'shiftround'. If almost all of a thousand-line file is indented by multiples +of 4 spaces, for example, but there is a single line with 3 spaces by +accident, this threshold should ignore such an outlier. The default is +5 (percent). + +AUTHOR *detect_indent-author* + +Written and maintained by Tom Ryder , who has friends who +use tabs where he doesn't, and vice-versa. + +LICENSE *detect_indent-license* + +Licensed for distribution under the same terms as Vim itself (see |license|). + + vim:tw=78:ts=8:ft=help:norl: diff --git a/plugin/detect_indent.vim b/plugin/detect_indent.vim new file mode 100644 index 0000000..e75324a --- /dev/null +++ b/plugin/detect_indent.vim @@ -0,0 +1,23 @@ +" +" detect_indent.vim: If 'expandtab' doesn't match the shape of the buffer's +" indents, switch it over, including a guess at 'shiftwidth' if switching it +" on. +" +" Author: Tom Ryder +" License: Same as Vim itself +" +if exists('loaded_detect_indent') || &compatible || v:version < 700 + finish +endif +let loaded_detect_indent = 1 + +" Set up a user command in case the user wants to set this manually +command! -bar DetectIndent + \ call detect_indent#() + +" Add hook for FileType event; this should load *after* filetype plugins +augroup detect_indent + autocmd! + autocmd FileType * + \ DetectIndent +augroup END -- cgit v1.2.3