Regenerate nvim config
This commit is contained in:
82
config/neovim/store/lazy-plugins/vim-sleuth/doc/sleuth.txt
Normal file
82
config/neovim/store/lazy-plugins/vim-sleuth/doc/sleuth.txt
Normal file
@ -0,0 +1,82 @@
|
||||
*sleuth.txt* Heuristically set buffer options
|
||||
|
||||
Author: Tim Pope <http://tpo.pe/>
|
||||
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*
|
||||
|
||||
Automatic detection happens on the |BufNewFile|, |BufReadPost|, and
|
||||
|BufFilePost| events. Sleuth first consults modelines, then EditorConfig
|
||||
(https://editorconfig.org) files. If neither of these produces a value for
|
||||
'expandtab' and 'shiftwidth', a heuristic algorithm is used to guess them,
|
||||
first on the current file, and then on other files with the same extension in
|
||||
the same and parent directories.
|
||||
|
||||
The full list of detected options is as follows:
|
||||
|
||||
Vim Option Safe? EditorConfig key ~
|
||||
'filetype' Y vim_filetype
|
||||
'expandtab' Y indent_style
|
||||
'shiftwidth' Y indent_size
|
||||
'tabstop' Y tab_width
|
||||
'textwidth' Y max_line_length
|
||||
'fixendofline' Y insert_final_newline
|
||||
'endofline' insert_final_newline
|
||||
'fileformat' end_of_line
|
||||
'fileencoding' charset
|
||||
'bomb' charset
|
||||
|
||||
Options marked "safe" are those guaranteed not to toggle 'modified'. These
|
||||
are the only options that will be set in 'nomodifiable' buffers, and the only
|
||||
options that Sleuth will detect in a modeline. Safe options will also be
|
||||
reapplied on every |FileType| event, so that they can override any |ftplugin|
|
||||
settings.
|
||||
|
||||
Sleuth always sets 'softtabstop' to -1, effectively mirroring 'shiftwidth'.
|
||||
|
||||
INTERFACE *sleuth-interface*
|
||||
|
||||
*:Sleuth*
|
||||
:Sleuth Manually detect indentation.
|
||||
|
||||
:verbose Sleuth Manually detect indentation, and display the reason
|
||||
each option was set.
|
||||
|
||||
*SleuthIndicator()*
|
||||
SleuthIndicator() Indicator for inclusion in 'statusline'. Or use
|
||||
flagship.vim to have it included automatically.
|
||||
|
||||
CONFIGURATION *sleuth-configuration*
|
||||
|
||||
To override detection and force options for a particular file or directory,
|
||||
use a |modeline| or EditorConfig (https://editorconfig.org/), respectively.
|
||||
|
||||
*g:sleuth_{filetype}_heuristics*
|
||||
To disable the heuristic algorithm for a given 'filetype', assign a false
|
||||
value to a variable named like g:sleuth_{filetype}_heuristics:
|
||||
>
|
||||
let g:sleuth_gitcommit_heuristics = 0
|
||||
<
|
||||
*g:sleuth_heuristics*
|
||||
Alternatively, one can disable heuristics by default, and opt-in for
|
||||
individual file types:
|
||||
>
|
||||
let g:sleuth_heuristics = 0
|
||||
let g:sleuth_perl_heuristics = 1
|
||||
<
|
||||
*g:sleuth_automatic*
|
||||
Sleuth used to support setting g:sleuth_automatic to a false value and instead
|
||||
calling :Sleuth selectively from |FileType| autocommands. This will no longer
|
||||
work quite right, and thus g:sleuth_automatic has been deprecated in favor of
|
||||
the heuristics options above.
|
||||
|
||||
*g:sleuth_no_filetype_indent_on*
|
||||
Sleuth forces |:filetype-indent-on| by default, which enables file-type
|
||||
specific indenting algorithms and is highly recommended. To opt out:
|
||||
>
|
||||
let g:sleuth_no_filetype_indent_on = 1
|
||||
<
|
||||
vim:tw=78:et:ft=help:norl:
|
||||
10
config/neovim/store/lazy-plugins/vim-sleuth/doc/tags
Normal file
10
config/neovim/store/lazy-plugins/vim-sleuth/doc/tags
Normal file
@ -0,0 +1,10 @@
|
||||
:Sleuth sleuth.txt /*:Sleuth*
|
||||
SleuthIndicator() sleuth.txt /*SleuthIndicator()*
|
||||
g:sleuth_automatic sleuth.txt /*g:sleuth_automatic*
|
||||
g:sleuth_heuristics sleuth.txt /*g:sleuth_heuristics*
|
||||
g:sleuth_no_filetype_indent_on sleuth.txt /*g:sleuth_no_filetype_indent_on*
|
||||
g:sleuth_{filetype}_heuristics sleuth.txt /*g:sleuth_{filetype}_heuristics*
|
||||
sleuth sleuth.txt /*sleuth*
|
||||
sleuth-configuration sleuth.txt /*sleuth-configuration*
|
||||
sleuth-interface sleuth.txt /*sleuth-interface*
|
||||
sleuth.txt sleuth.txt /*sleuth.txt*
|
||||
671
config/neovim/store/lazy-plugins/vim-sleuth/plugin/sleuth.vim
Normal file
671
config/neovim/store/lazy-plugins/vim-sleuth/plugin/sleuth.vim
Normal file
@ -0,0 +1,671 @@
|
||||
" sleuth.vim - Heuristically set buffer options
|
||||
" Maintainer: Tim Pope <http://tpo.pe/>
|
||||
" Version: 2.0
|
||||
" GetLatestVimScripts: 4375 1 :AutoInstall: sleuth.vim
|
||||
|
||||
if exists("#polyglot-sleuth")
|
||||
autocmd! polyglot-sleuth
|
||||
augroup! polyglot-sleuth
|
||||
unlet! g:loaded_sleuth
|
||||
let s:polyglot = 1
|
||||
endif
|
||||
|
||||
if exists("g:loaded_sleuth") || v:version < 700 || &cp
|
||||
finish
|
||||
endif
|
||||
let g:loaded_sleuth = 1
|
||||
lockvar g:loaded_sleuth
|
||||
|
||||
function! s:Warn(msg, ...) abort
|
||||
if !get(a:000, 0, 0)
|
||||
echohl WarningMsg
|
||||
echo a:msg
|
||||
echohl NONE
|
||||
endif
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
if exists('+shellslash')
|
||||
function! s:Slash(path) abort
|
||||
return tr(a:path, '\', '/')
|
||||
endfunction
|
||||
else
|
||||
function! s:Slash(path) abort
|
||||
return a:path
|
||||
endfunction
|
||||
endif
|
||||
|
||||
function! s:Guess(source, detected, lines) abort
|
||||
let has_heredocs = a:detected.filetype =~# '^\%(perl\|php\|ruby\|[cz]\=sh\|bash\)$'
|
||||
let options = {}
|
||||
let heuristics = {'spaces': 0, 'hard': 0, 'soft': 0, 'checked': 0, 'indents': {}}
|
||||
let tabstop = get(a:detected.options, 'tabstop', get(a:detected.defaults, 'tabstop', [8]))[0]
|
||||
let softtab = repeat(' ', tabstop)
|
||||
let waiting_on = ''
|
||||
let prev_indent = -1
|
||||
let prev_line = ''
|
||||
|
||||
for line in a:lines
|
||||
if len(waiting_on)
|
||||
if line =~# waiting_on
|
||||
let waiting_on = ''
|
||||
let prev_indent = -1
|
||||
let prev_line = ''
|
||||
endif
|
||||
continue
|
||||
elseif line =~# '^\s*$'
|
||||
continue
|
||||
elseif a:detected.filetype ==# 'python' && prev_line[-1:-1] =~# '[[\({]'
|
||||
let prev_indent = -1
|
||||
let prev_line = ''
|
||||
continue
|
||||
elseif line =~# '^=\w' && line !~# '^=\%(end\|cut\)\>'
|
||||
let waiting_on = '^=\%(end\|cut\)\>'
|
||||
elseif line =~# '^@@\+ -\d\+,\d\+ '
|
||||
let waiting_on = '^$'
|
||||
elseif line !~# '[/<"`]'
|
||||
" No need to do other checks
|
||||
elseif line =~# '^\s*/\*' && line !~# '\*/'
|
||||
let waiting_on = '\*/'
|
||||
elseif line =~# '^\s*<\!--' && line !~# '-->'
|
||||
let waiting_on = '-->'
|
||||
elseif line =~# '^[^"]*"""[^"]*$'
|
||||
let waiting_on = '^[^"]*"""[^"]*$'
|
||||
elseif a:detected.filetype ==# 'go' && line =~# '^[^`]*`[^`]*$'
|
||||
let waiting_on = '^[^`]*`[^`]*$'
|
||||
elseif has_heredocs
|
||||
let waiting_on = matchstr(line, '<<\s*\([''"]\=\)\zs\w\+\ze\1[^''"`<>]*$')
|
||||
if len(waiting_on)
|
||||
let waiting_on = '^' . waiting_on . '$'
|
||||
endif
|
||||
endif
|
||||
|
||||
let indent = len(matchstr(substitute(line, '\t', softtab, 'g'), '^ *'))
|
||||
if line =~# '^\t'
|
||||
let heuristics.hard += 1
|
||||
elseif line =~# '^' . softtab
|
||||
let heuristics.soft += 1
|
||||
endif
|
||||
if line =~# '^ '
|
||||
let heuristics.spaces += 1
|
||||
endif
|
||||
let increment = prev_indent < 0 ? 0 : indent - prev_indent
|
||||
let prev_indent = indent
|
||||
let prev_line = line
|
||||
if increment > 1 && (increment < 4 || increment % 4 == 0)
|
||||
if has_key(heuristics.indents, increment)
|
||||
let heuristics.indents[increment] += 1
|
||||
else
|
||||
let heuristics.indents[increment] = 1
|
||||
endif
|
||||
let heuristics.checked += 1
|
||||
endif
|
||||
if heuristics.checked >= 32 && (heuristics.hard > 3 || heuristics.soft > 3) && get(heuristics.indents, increment) * 2 > heuristics.checked
|
||||
if heuristics.spaces
|
||||
break
|
||||
elseif !exists('no_space_indent')
|
||||
let no_space_indent = stridx("\n" . join(a:lines, "\n"), "\n ") < 0
|
||||
if no_space_indent
|
||||
break
|
||||
endif
|
||||
endif
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
let a:detected.heuristics[a:source] = heuristics
|
||||
|
||||
let max_frequency = 0
|
||||
for [shiftwidth, frequency] in items(heuristics.indents)
|
||||
if frequency > max_frequency || frequency == max_frequency && +shiftwidth < get(options, 'shiftwidth')
|
||||
let options.shiftwidth = +shiftwidth
|
||||
let max_frequency = frequency
|
||||
endif
|
||||
endfor
|
||||
|
||||
if heuristics.hard && !heuristics.spaces &&
|
||||
\ !has_key(a:detected.options, 'tabstop')
|
||||
let options = {'expandtab': 0, 'shiftwidth': 0}
|
||||
elseif heuristics.hard > heuristics.soft
|
||||
let options.expandtab = 0
|
||||
let options.tabstop = tabstop
|
||||
else
|
||||
if heuristics.soft
|
||||
let options.expandtab = 1
|
||||
endif
|
||||
if heuristics.hard || has_key(a:detected.options, 'tabstop') ||
|
||||
\ stridx(join(a:lines, "\n"), "\t") >= 0
|
||||
let options.tabstop = tabstop
|
||||
elseif !&g:shiftwidth && has_key(options, 'shiftwidth') &&
|
||||
\ !has_key(a:detected.options, 'shiftwidth')
|
||||
let options.tabstop = options.shiftwidth
|
||||
let options.shiftwidth = 0
|
||||
endif
|
||||
endif
|
||||
|
||||
call map(options, '[v:val, a:source]')
|
||||
call extend(a:detected.options, options, 'keep')
|
||||
endfunction
|
||||
|
||||
function! s:Capture(cmd) abort
|
||||
redir => capture
|
||||
silent execute a:cmd
|
||||
redir END
|
||||
return capture
|
||||
endfunction
|
||||
|
||||
let s:modeline_numbers = {
|
||||
\ 'shiftwidth': 'shiftwidth', 'sw': 'shiftwidth',
|
||||
\ 'tabstop': 'tabstop', 'ts': 'tabstop',
|
||||
\ 'textwidth': 'textwidth', 'tw': 'textwidth',
|
||||
\ }
|
||||
let s:modeline_booleans = {
|
||||
\ 'expandtab': 'expandtab', 'et': 'expandtab',
|
||||
\ 'fixendofline': 'fixendofline', 'fixeol': 'fixendofline',
|
||||
\ }
|
||||
function! s:ParseOptions(declarations, into, ...) abort
|
||||
for option in a:declarations
|
||||
if has_key(s:modeline_booleans, matchstr(option, '^\%(no\)\=\zs\w\+$'))
|
||||
let a:into[s:modeline_booleans[matchstr(option, '^\%(no\)\=\zs\w\+')]] = [option !~# '^no'] + a:000
|
||||
elseif has_key(s:modeline_numbers, matchstr(option, '^\w\+\ze=[1-9]\d*$'))
|
||||
let a:into[s:modeline_numbers[matchstr(option, '^\w\+')]] = [str2nr(matchstr(option, '\d\+$'))] + a:000
|
||||
elseif option =~# '^\%(ft\|filetype\)=[[:alnum:]._-]*$'
|
||||
let a:into.filetype = [matchstr(option, '=\zs.*')] + a:000
|
||||
endif
|
||||
if option ==# 'nomodeline' || option ==# 'noml'
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! s:ModelineOptions() abort
|
||||
let options = {}
|
||||
if !&l:modeline && (&g:modeline || s:Capture('setlocal') =~# '\\\@<![[:space:]]nomodeline\>' &&
|
||||
\ s:Capture('verbose setglobal modeline?') !=# s:Capture('verbose setlocal modeline?'))
|
||||
return options
|
||||
endif
|
||||
let modelines = get(b:, 'sleuth_modelines', get(g:, 'sleuth_modelines', 5))
|
||||
if line('$') > 2 * modelines
|
||||
let lnums = range(1, modelines) + range(line('$') - modelines + 1, line('$'))
|
||||
else
|
||||
let lnums = range(1, line('$'))
|
||||
endif
|
||||
for lnum in lnums
|
||||
if s:ParseOptions(split(matchstr(getline(lnum),
|
||||
\ '\%(\S\@<!vim\=\|\s\@<=ex\):\s*\(set\= \zs[^:]\+\|\zs.*\S\)'),
|
||||
\ '[[:space:]:]\+'), options, 'modeline', lnum)
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
return options
|
||||
endfunction
|
||||
|
||||
let s:fnmatch_replacements = {
|
||||
\ '.': '\.', '\%': '%', '\(': '(', '\)': ')', '\{': '{', '\}': '}', '\_': '_',
|
||||
\ '?': '[^/]', '*': '[^/]*', '/**/*': '/.*', '/**/': '/\%(.*/\)\=', '**': '.*'}
|
||||
function! s:FnmatchReplace(pat) abort
|
||||
if has_key(s:fnmatch_replacements, a:pat)
|
||||
return s:fnmatch_replacements[a:pat]
|
||||
elseif len(a:pat) ==# 1
|
||||
return '\' . a:pat
|
||||
elseif a:pat =~# '^{[+-]\=\d\+\.\.[+-]\=\d\+}$'
|
||||
return '\%(' . join(range(matchstr(a:pat, '[+-]\=\d\+'), matchstr(a:pat, '\.\.\zs[+-]\=\d\+')), '\|') . '\)'
|
||||
elseif a:pat =~# '^{.*\\\@<!\%(\\\\\)*,.*}$'
|
||||
return '\%(' . substitute(a:pat[1:-2], ',\|\%(\\.\|{[^\{}]*}\|[^,]\)*', '\=submatch(0) ==# "," ? "\\|" : s:FnmatchTranslate(submatch(0))', 'g') . '\)'
|
||||
elseif a:pat =~# '^{.*}$'
|
||||
return '{' . s:FnmatchTranslate(a:pat[1:-2]) . '}'
|
||||
elseif a:pat =~# '^\[!'
|
||||
return '[^' . a:pat[2:-1]
|
||||
else
|
||||
return a:pat
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:FnmatchTranslate(pat) abort
|
||||
return substitute(a:pat, '\\.\|/\*\*/\*\=\|\*\*\=\|\[[!^]\=\]\=[^]/]*\]\|{\%(\\.\|[^{}]\|{[^\{}]*}\)*}\|[?.\~^$[]', '\=s:FnmatchReplace(submatch(0))', 'g')
|
||||
endfunction
|
||||
|
||||
function! s:ReadEditorConfig(absolute_path) abort
|
||||
try
|
||||
let lines = readfile(a:absolute_path)
|
||||
catch
|
||||
let lines = []
|
||||
endtry
|
||||
let prefix = '\m\C^' . escape(fnamemodify(a:absolute_path, ':h'), '][^$.*\~')
|
||||
let preamble = {}
|
||||
let pairs = preamble
|
||||
let sections = []
|
||||
let i = 0
|
||||
while i < len(lines)
|
||||
let line = lines[i]
|
||||
let i += 1
|
||||
let line = substitute(line, '^[[:space:]]*\|[[:space:]]*\%([^[:space:]]\@<![;#].*\)\=$', '', 'g')
|
||||
let match = matchlist(line, '^\%(\[\(\%(\\.\|[^\;#]\)*\)\]\|\([^[:space:]]\@=[^;#=:]*[^;#=:[:space:]]\)[[:space:]]*[=:][[:space:]]*\(.*\)\)$')
|
||||
if len(get(match, 2, ''))
|
||||
let pairs[tolower(match[2])] = [match[3], a:absolute_path, i]
|
||||
elseif len(get(match, 1, '')) && len(get(match, 1, '')) <= 4096
|
||||
if match[1] =~# '^/'
|
||||
let pattern = match[1]
|
||||
elseif match[1] =~# '/'
|
||||
let pattern = '/' . match[1]
|
||||
else
|
||||
let pattern = '/**/' . match[1]
|
||||
endif
|
||||
let pairs = {}
|
||||
call add(sections, [prefix . s:FnmatchTranslate(pattern) . '$', pairs])
|
||||
endif
|
||||
endwhile
|
||||
return [preamble, sections]
|
||||
endfunction
|
||||
|
||||
let s:editorconfig_cache = {}
|
||||
function! s:DetectEditorConfig(absolute_path, ...) abort
|
||||
if empty(a:absolute_path)
|
||||
return [{}, '']
|
||||
endif
|
||||
let root = ''
|
||||
let tail = a:0 ? a:1 : '.editorconfig'
|
||||
let dir = fnamemodify(a:absolute_path, ':h')
|
||||
let previous_dir = ''
|
||||
let sections = []
|
||||
let overrides = get(g:, 'sleuth_editorconfig_overrides', {})
|
||||
while dir !=# previous_dir && dir !~# '^//\%([^/]\+/\=\)\=$'
|
||||
let head = substitute(dir, '/\=$', '/', '')
|
||||
let read_from = get(overrides, head . tail, get(overrides, head, head . tail))
|
||||
if read_from is# ''
|
||||
break
|
||||
elseif type(read_from) == type('') && read_from !=# head . tail && read_from !~# '^/\|^\a\+:'
|
||||
let read_from = simplify(head . read_from)
|
||||
endif
|
||||
let ftime = type(read_from) == type('') ? getftime(read_from) : -1
|
||||
let [cachetime; econfig] = get(s:editorconfig_cache, read_from, [-1, {}, []])
|
||||
if ftime != cachetime
|
||||
let econfig = s:ReadEditorConfig(read_from)
|
||||
let s:editorconfig_cache[read_from] = [ftime] + econfig
|
||||
lockvar! s:editorconfig_cache[read_from]
|
||||
unlockvar s:editorconfig_cache[read_from]
|
||||
endif
|
||||
call extend(sections, econfig[1], 'keep')
|
||||
if get(econfig[0], 'root', [''])[0] ==? 'true'
|
||||
let root = head
|
||||
break
|
||||
endif
|
||||
let previous_dir = dir
|
||||
let dir = fnamemodify(dir, ':h')
|
||||
endwhile
|
||||
|
||||
let config = {}
|
||||
for [pattern, pairs] in sections
|
||||
if a:absolute_path =~# pattern
|
||||
call extend(config, pairs)
|
||||
endif
|
||||
endfor
|
||||
|
||||
return [config, root]
|
||||
endfunction
|
||||
|
||||
let s:editorconfig_bomb = {
|
||||
\ 'utf-8': 0,
|
||||
\ 'utf-8-bom': 1,
|
||||
\ 'utf-16be': 1,
|
||||
\ 'utf-16le': 1,
|
||||
\ 'latin1': 0,
|
||||
\ }
|
||||
|
||||
let s:editorconfig_fileformat = {
|
||||
\ 'cr': 'mac',
|
||||
\ 'crlf': 'dos',
|
||||
\ 'lf': 'unix',
|
||||
\ }
|
||||
|
||||
function! s:EditorConfigToOptions(pairs) abort
|
||||
let options = {}
|
||||
let pairs = map(copy(a:pairs), 'v:val[0]')
|
||||
let sources = map(copy(a:pairs), 'v:val[1:-1]')
|
||||
call filter(pairs, 'v:val !=? "unset"')
|
||||
|
||||
if get(pairs, 'indent_style', '') ==? 'tab'
|
||||
let options.expandtab = [0] + sources.indent_style
|
||||
elseif get(pairs, 'indent_style', '') ==? 'space'
|
||||
let options.expandtab = [1] + sources.indent_style
|
||||
endif
|
||||
|
||||
if get(pairs, 'indent_size', '') =~? '^[1-9]\d*$\|^tab$'
|
||||
let options.shiftwidth = [str2nr(pairs.indent_size)] + sources.indent_size
|
||||
if &g:shiftwidth == 0 && !has_key(pairs, 'tab_width') && pairs.indent_size !=? 'tab'
|
||||
let options.tabstop = options.shiftwidth
|
||||
let options.shiftwidth = [0] + sources.indent_size
|
||||
endif
|
||||
endif
|
||||
|
||||
if get(pairs, 'tab_width', '') =~? '^[1-9]\d*$'
|
||||
let options.tabstop = [str2nr(pairs.tab_width)] + sources.tab_width
|
||||
if !has_key(pairs, 'indent_size') && get(pairs, 'indent_style', '') ==? 'tab'
|
||||
let options.shiftwidth = [0] + options.tabstop[1:-1]
|
||||
endif
|
||||
endif
|
||||
|
||||
if get(pairs, 'max_line_length', '') =~? '^[1-9]\d*$\|^off$'
|
||||
let options.textwidth = [str2nr(pairs.max_line_length)] + sources.max_line_length
|
||||
endif
|
||||
|
||||
if get(pairs, 'insert_final_newline', '') =~? '^true$\|^false$'
|
||||
let options.endofline = [pairs.insert_final_newline ==? 'true'] + sources.insert_final_newline
|
||||
let options.fixendofline = copy(options.endofline)
|
||||
endif
|
||||
|
||||
let eol = tolower(get(pairs, 'end_of_line', ''))
|
||||
if has_key(s:editorconfig_fileformat, eol)
|
||||
let options.fileformat = [s:editorconfig_fileformat[eol]] + sources.end_of_line
|
||||
endif
|
||||
|
||||
let charset = tolower(get(pairs, 'charset', ''))
|
||||
if has_key(s:editorconfig_bomb, charset)
|
||||
let options.bomb = [s:editorconfig_bomb[charset]] + sources.charset
|
||||
let options.fileencoding = [substitute(charset, '\C-bom$', '', '')] + sources.charset
|
||||
endif
|
||||
|
||||
let filetype = tolower(get(pairs, 'vim_filetype', 'unset'))
|
||||
if filetype !=# 'unset' && filetype =~# '^[.a-z0-9_-]*$'
|
||||
let options.filetype = [substitute(filetype, '^\.\+\|\.\+$', '', 'g')] + sources.vim_filetype
|
||||
endif
|
||||
|
||||
return options
|
||||
endfunction
|
||||
|
||||
function! s:Ready(detected) abort
|
||||
return has_key(a:detected.options, 'expandtab') && has_key(a:detected.options, 'shiftwidth')
|
||||
endfunction
|
||||
|
||||
let s:booleans = {'expandtab': 1, 'fixendofline': 1, 'endofline': 1, 'bomb': 1}
|
||||
let s:safe_options = ['expandtab', 'shiftwidth', 'tabstop', 'textwidth', 'fixendofline']
|
||||
let s:all_options = s:safe_options + ['endofline', 'fileformat', 'fileencoding', 'bomb']
|
||||
let s:short_options = {
|
||||
\ 'expandtab': 'et', 'shiftwidth': 'sw', 'tabstop': 'ts',
|
||||
\ 'textwidth': 'tw', 'fixendofline': 'fixeol',
|
||||
\ 'endofline': 'eol', 'fileformat': 'ff', 'fileencoding': 'fenc'}
|
||||
|
||||
function! s:Apply(detected, permitted_options, silent) abort
|
||||
let options = extend(copy(a:detected.defaults), a:detected.options)
|
||||
if get(a:detected.defaults, 'shiftwidth', [1])[0] == 0 && get(options, 'shiftwidth', [0])[0] != 0 && !has_key(a:detected.declared, 'tabstop')
|
||||
let options.tabstop = options.shiftwidth
|
||||
let options.shiftwidth = a:detected.defaults.shiftwidth
|
||||
endif
|
||||
if has_key(options, 'shiftwidth') && !has_key(options, 'expandtab')
|
||||
let options.expandtab = [stridx(join(getline(1, 256), "\n"), "\t") == -1, a:detected.bufname]
|
||||
endif
|
||||
if !exists('*shiftwidth') && !get(options, 'shiftwidth', [1])[0]
|
||||
let options.shiftwidth = [get(options, 'tabstop', [&tabstop])[0]] + options.shiftwidth[1:-1]
|
||||
endif
|
||||
let msg = ''
|
||||
let cmd = 'setlocal'
|
||||
for option in a:permitted_options
|
||||
if !exists('&' . option) || !has_key(options, option) ||
|
||||
\ !&l:modifiable && index(s:safe_options, option) == -1
|
||||
continue
|
||||
endif
|
||||
let value = options[option]
|
||||
if has_key(s:booleans, option)
|
||||
let setting = (value[0] ? '' : 'no') . option
|
||||
else
|
||||
let setting = option . '=' . value[0]
|
||||
endif
|
||||
if getbufvar('', '&' . option) !=# value[0] || index(s:safe_options, option) >= 0
|
||||
let cmd .= ' ' . setting
|
||||
endif
|
||||
if !&verbose || a:silent
|
||||
if has_key(s:booleans, option)
|
||||
let msg .= ' ' . (value[0] ? '' : 'no') . get(s:short_options, option, option)
|
||||
else
|
||||
let msg .= ' ' . get(s:short_options, option, option) . '=' . value[0]
|
||||
endif
|
||||
continue
|
||||
endif
|
||||
if len(value) > 1
|
||||
if value[1] ==# a:detected.bufname
|
||||
let file = '%'
|
||||
else
|
||||
let file = value[1] =~# '/' ? fnamemodify(value[1], ':~:.') : value[1]
|
||||
if file !=# value[1] && file[0:0] !=# '~'
|
||||
let file = './' . file
|
||||
endif
|
||||
endif
|
||||
if len(value) > 2
|
||||
let file .= ' line ' . value[2]
|
||||
endif
|
||||
echo printf(':setlocal %-21s " from %s', setting, file)
|
||||
else
|
||||
echo ':setlocal ' . setting
|
||||
endif
|
||||
endfor
|
||||
if !&verbose && !empty(msg) && !a:silent
|
||||
echo ':setlocal' . msg
|
||||
endif
|
||||
if has_key(options, 'shiftwidth')
|
||||
let cmd .= ' softtabstop=' . (exists('*shiftwidth') ? -1 : options.shiftwidth[0])
|
||||
else
|
||||
call s:Warn(':Sleuth failed to detect indent settings', a:silent)
|
||||
endif
|
||||
return cmd ==# 'setlocal' ? '' : cmd
|
||||
endfunction
|
||||
|
||||
function! s:UserOptions(ft, name) abort
|
||||
if exists('b:sleuth_' . a:name)
|
||||
let source = 'b:sleuth_' . a:name
|
||||
elseif exists('g:sleuth_' . a:ft . '_' . a:name)
|
||||
let source = 'g:sleuth_' . a:ft . '_' . a:name
|
||||
endif
|
||||
if !exists('l:source') || type(eval(source)) == type(function('tr'))
|
||||
return {}
|
||||
endif
|
||||
let val = eval(source)
|
||||
let options = {}
|
||||
if type(val) == type('')
|
||||
call s:ParseOptions(split(substitute(val, '\S\@<![=+]\S\@=', 'ft=', 'g'), '[[:space:]:,]\+'), options, source)
|
||||
if has_key(options, 'filetype')
|
||||
call extend(options, s:UserOptions(remove(options, 'filetype')[0], a:name), 'keep')
|
||||
endif
|
||||
if has_key(options, 'tabstop')
|
||||
call extend(options, {'shiftwidth': [0, source], 'expandtab': [0, source]}, 'keep')
|
||||
elseif has_key(options, 'shiftwidth')
|
||||
call extend(options, {'expandtab': [1, source]}, 'keep')
|
||||
endif
|
||||
elseif type(val) == type([])
|
||||
call s:ParseOptions(val, options, source)
|
||||
endif
|
||||
call filter(options, 'index(s:safe_options, v:key) >= 0')
|
||||
return options
|
||||
endfunction
|
||||
|
||||
function! s:DetectDeclared() abort
|
||||
let detected = {'bufname': s:Slash(@%), 'declared': {}}
|
||||
let absolute_or_empty = detected.bufname =~# '^$\|^\a\+:\|^/'
|
||||
if &l:buftype =~# '^\%(nowrite\)\=$' && !absolute_or_empty
|
||||
let detected.bufname = substitute(s:Slash(getcwd()), '/\=$', '/', '') . detected.bufname
|
||||
let absolute_or_empty = 1
|
||||
endif
|
||||
let detected.path = absolute_or_empty ? detected.bufname : ''
|
||||
let pre = substitute(matchstr(detected.path, '^\a\a\+\ze:'), '^\a', '\u&', 'g')
|
||||
if len(pre) && exists('*' . pre . 'Real')
|
||||
let detected.path = s:Slash(call(pre . 'Real', [detected.path]))
|
||||
endif
|
||||
|
||||
try
|
||||
if len(detected.path) && exists('*ExcludeBufferFromDiscovery') && !empty(ExcludeBufferFromDiscovery(detected.path, 'sleuth'))
|
||||
let detected.path = ''
|
||||
endif
|
||||
catch
|
||||
endtry
|
||||
let [detected.editorconfig, detected.root] = s:DetectEditorConfig(detected.path)
|
||||
call extend(detected.declared, s:EditorConfigToOptions(detected.editorconfig))
|
||||
call extend(detected.declared, s:ModelineOptions())
|
||||
return detected
|
||||
endfunction
|
||||
|
||||
function! s:DetectHeuristics(into) abort
|
||||
let detected = a:into
|
||||
let filetype = split(&l:filetype, '\.', 1)[0]
|
||||
if get(detected, 'filetype', '*') ==# filetype
|
||||
return detected
|
||||
endif
|
||||
let detected.filetype = filetype
|
||||
let options = copy(detected.declared)
|
||||
let detected.options = options
|
||||
let detected.heuristics = {}
|
||||
if has_key(detected, 'patterns')
|
||||
call remove(detected, 'patterns')
|
||||
endif
|
||||
let detected.defaults = s:UserOptions(filetype, 'defaults')
|
||||
if empty(filetype) || !get(b:, 'sleuth_automatic', 1) || empty(get(b:, 'sleuth_heuristics', get(g:, 'sleuth_' . filetype . '_heuristics', get(g:, 'sleuth_heuristics', 1))))
|
||||
return detected
|
||||
endif
|
||||
if s:Ready(detected)
|
||||
return detected
|
||||
endif
|
||||
|
||||
let lines = getline(1, 1024)
|
||||
call s:Guess(detected.bufname, detected, lines)
|
||||
if s:Ready(detected)
|
||||
return detected
|
||||
elseif get(options, 'shiftwidth', [4])[0] < 4 && stridx(join(lines, "\n"), "\t") == -1
|
||||
let options.expandtab = [1, detected.bufname]
|
||||
return detected
|
||||
endif
|
||||
let dir = len(detected.path) ? fnamemodify(detected.path, ':h') : ''
|
||||
let root = len(detected.root) ? fnamemodify(detected.root, ':h') : dir ==# s:Slash(expand('~')) ? dir : fnamemodify(dir, ':h')
|
||||
if detected.bufname =~# '^\a\a\+:' || root ==# '.' || !isdirectory(root)
|
||||
let dir = ''
|
||||
endif
|
||||
let c = get(b:, 'sleuth_neighbor_limit', get(g:, 'sleuth_neighbor_limit', 8))
|
||||
if c <= 0 || empty(dir)
|
||||
let detected.patterns = []
|
||||
elseif type(get(b:, 'sleuth_globs')) == type([])
|
||||
let detected.patterns = b:sleuth_globs
|
||||
elseif type(get(g:, 'sleuth_' . detected.filetype . '_globs')) == type([])
|
||||
let detected.patterns = get(g:, 'sleuth_' . detected.filetype . '_globs')
|
||||
else
|
||||
let detected.patterns = ['*' . matchstr(detected.bufname, '/\@<!\.[^][{}*?$~\`./]\+$')]
|
||||
if detected.patterns ==# ['*']
|
||||
let detected.patterns = [matchstr(detected.bufname, '/\zs[^][{}*?$~\`/]\+\ze/\=$')]
|
||||
let dir = fnamemodify(dir, ':h')
|
||||
if empty(detected.patterns[0])
|
||||
let detected.patterns = []
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
while c > 0 && dir !~# '^$\|^//[^/]*$' && dir !=# fnamemodify(dir, ':h')
|
||||
for pattern in detected.patterns
|
||||
for neighbor in split(glob(dir.'/'.pattern), "\n")[0:7]
|
||||
if neighbor !=# detected.path && filereadable(neighbor)
|
||||
call s:Guess(neighbor, detected, readfile(neighbor, '', 256))
|
||||
let c -= 1
|
||||
endif
|
||||
if s:Ready(detected)
|
||||
return detected
|
||||
endif
|
||||
if c <= 0
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
if c <= 0
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
if len(dir) <= len(root)
|
||||
break
|
||||
endif
|
||||
let dir = fnamemodify(dir, ':h')
|
||||
endwhile
|
||||
if !has_key(options, 'shiftwidth')
|
||||
let detected.options = copy(detected.declared)
|
||||
endif
|
||||
return detected
|
||||
endfunction
|
||||
|
||||
function! s:Init(redetect, unsafe, do_filetype, silent) abort
|
||||
if !a:redetect && exists('b:sleuth.defaults')
|
||||
let detected = b:sleuth
|
||||
endif
|
||||
unlet! b:sleuth
|
||||
if &l:buftype !~# '^\%(nowrite\|nofile\|acwrite\)\=$'
|
||||
return s:Warn(':Sleuth disabled for buftype=' . &l:buftype, a:silent)
|
||||
endif
|
||||
if &l:filetype ==# 'netrw'
|
||||
return s:Warn(':Sleuth disabled for filetype=' . &l:filetype, a:silent)
|
||||
endif
|
||||
if &l:binary
|
||||
return s:Warn(':Sleuth disabled for binary files', a:silent)
|
||||
endif
|
||||
if !exists('detected')
|
||||
let detected = s:DetectDeclared()
|
||||
endif
|
||||
let setfiletype = ''
|
||||
if a:do_filetype && has_key(detected.declared, 'filetype')
|
||||
let filetype = detected.declared.filetype[0]
|
||||
if filetype !=# &l:filetype || empty(filetype)
|
||||
let setfiletype = 'setlocal filetype=' . filetype
|
||||
else
|
||||
let setfiletype = 'setfiletype ' . filetype
|
||||
endif
|
||||
endif
|
||||
exe setfiletype
|
||||
call s:DetectHeuristics(detected)
|
||||
let cmd = s:Apply(detected, (a:do_filetype ? ['filetype'] : []) + (a:unsafe ? s:all_options : s:safe_options), a:silent)
|
||||
let b:sleuth = detected
|
||||
if exists('s:polyglot') && !a:silent
|
||||
call s:Warn('Charlatan :Sleuth implementation in vim-polyglot has been found and disabled.')
|
||||
call s:Warn('To get rid of this message, uninstall vim-polyglot, or disable the')
|
||||
call s:Warn('corresponding feature in your vimrc:')
|
||||
call s:Warn(' let g:polyglot_disabled = ["autoindent"]')
|
||||
endif
|
||||
return cmd
|
||||
endfunction
|
||||
|
||||
function! s:AutoInit() abort
|
||||
return s:Init(1, 1, 1, 1)
|
||||
endfunction
|
||||
|
||||
function! s:Sleuth(line1, line2, range, bang, mods, args) abort
|
||||
let safe = a:bang || expand("<sfile>") =~# '\%(^\|\.\.\)FileType '
|
||||
return s:Init(!a:bang, !safe, !safe, 0)
|
||||
endfunction
|
||||
|
||||
if !exists('g:did_indent_on') && !get(g:, 'sleuth_no_filetype_indent_on')
|
||||
filetype indent on
|
||||
elseif !exists('g:did_load_filetypes')
|
||||
filetype on
|
||||
endif
|
||||
|
||||
function! SleuthIndicator() abort
|
||||
let sw = &shiftwidth ? &shiftwidth : &tabstop
|
||||
if &expandtab
|
||||
let ind = 'sw='.sw
|
||||
elseif &tabstop == sw
|
||||
let ind = 'ts='.&tabstop
|
||||
else
|
||||
let ind = 'sw='.sw.',ts='.&tabstop
|
||||
endif
|
||||
if &textwidth
|
||||
let ind .= ',tw='.&textwidth
|
||||
endif
|
||||
if exists('&fixendofline') && !&fixendofline && !&endofline
|
||||
let ind .= ',noeol'
|
||||
endif
|
||||
return ind
|
||||
endfunction
|
||||
|
||||
augroup sleuth
|
||||
autocmd!
|
||||
autocmd BufNewFile,BufReadPost * nested
|
||||
\ if get(g:, 'sleuth_automatic', 1)
|
||||
\ | exe s:AutoInit() | endif
|
||||
autocmd BufFilePost * nested
|
||||
\ if (@% !~# '^!' || exists('b:sleuth')) && get(g:, 'sleuth_automatic', 1)
|
||||
\ | exe s:AutoInit() | endif
|
||||
autocmd FileType * nested
|
||||
\ if exists('b:sleuth') | exe s:Init(0, 0, 0, 1) | endif
|
||||
autocmd User Flags call Hoist('buffer', 5, 'SleuthIndicator')
|
||||
augroup END
|
||||
|
||||
command! -bar -bang Sleuth exe s:Sleuth(<line1>, <count>, +"<range>", <bang>0, "<mods>", <q-args>)
|
||||
Reference in New Issue
Block a user