aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Ryder <tom@sanctum.geek.nz>2020-05-30 19:21:03 +1200
committerTom Ryder <tom@sanctum.geek.nz>2020-05-30 19:21:03 +1200
commit4b364f096f9c5ad06daa99f5d302e0b07424f0bf (patch)
treea2dc101edc23e2a27cca1d6f6213201da954ca8d
downloadvim-detect-indent-0.1.0.tar.gz (sig)
vim-detect-indent-0.1.0.zip
First version, spun out from dotfiles v9.10.0v0.1.0
-rw-r--r--README.md0
-rw-r--r--VERSION1
-rw-r--r--autoload/detect_indent.vim108
-rw-r--r--doc/detect_indent.txt48
-rw-r--r--plugin/detect_indent.vim23
5 files changed, 180 insertions, 0 deletions
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/README.md
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 <tom@sanctum.geek.nz>, 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 <tom@sanctum.geek.nz>
+" 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