dotfiles/vim/plugins/neco-vim/autoload/necovim/helper.vim
2018-04-05 13:06:54 +02:00

674 lines
20 KiB
VimL

"=============================================================================
" FILE: helper.vim
" AUTHOR: Shougo Matsushita <Shougo.Matsu@gmail.com>
" License: MIT license
"=============================================================================
if !exists('s:internal_candidates_list')
let s:internal_candidates_list = {}
let s:global_candidates_list = {
\ 'dictionary_variables' : {}, 'runtimepath' : &runtimepath }
let s:script_candidates_list = {}
let s:local_candidates_list = {}
endif
let s:dictionary_path =
\ substitute(fnamemodify(expand('<sfile>'), ':h'), '\\', '/', 'g')
function! necovim#helper#make_cache() abort
if &filetype !=# 'vim'
return
endif
let s:script_candidates_list[bufnr('%')] =
\ s:get_script_candidates(bufnr('%'))
endfunction
function! necovim#helper#augroup(cur_text, complete_str) abort
" Make cache.
if s:check_global_candidates('augroups')
let s:global_candidates_list.augroups = s:get_augrouplist()
endif
return copy(s:global_candidates_list.augroups)
endfunction
function! necovim#helper#colorscheme_args(cur_text, complete_str) abort
return s:make_completion_list(map(split(
\ globpath(&runtimepath, 'colors/*.vim'), '\n'),
\ 'fnamemodify(v:val, ":t:r")'))
endfunction
function! necovim#helper#command(cur_text, complete_str) abort
if a:cur_text == '' ||
\ a:cur_text =~ '^[[:digit:],[:space:][:tab:]$''<>]*\h\w*$'
" Commands.
" Make cache.
if s:check_global_candidates('commands')
let s:global_candidates_list.commands = s:get_cmdlist()
endif
if !has_key(s:internal_candidates_list, 'commands')
let s:internal_candidates_list.commands = s:make_cache_commands()
endif
let list = copy(s:internal_candidates_list.commands)
\ + copy(s:global_candidates_list.commands)
else
" Commands args.
" Expression.
let list = necovim#helper#expression(a:cur_text, a:complete_str)
if s:has_cmdline()
let list += s:make_completion_list(
\ getcompletion(a:cur_text, 'cmdline'))
let list = s:uniq_by(list, 'v:val.word')
endif
endif
return list
endfunction
function! necovim#helper#environment(cur_text, complete_str) abort
" Make cache.
if s:check_global_candidates('environments')
let s:global_candidates_list.environments = s:get_envlist()
endif
return copy(s:global_candidates_list.environments)
endfunction
function! necovim#helper#expand(cur_text, complete_str) abort
return s:make_completion_list(
\ ['<cfile>', '<afile>', '<abuf>', '<amatch>',
\ '<sfile>', '<cword>', '<cWORD>', '<client>'])
endfunction
function! necovim#helper#expression(cur_text, complete_str) abort
return necovim#helper#function(a:cur_text, a:complete_str)
\+ necovim#helper#var(a:cur_text, a:complete_str)
endfunction
function! necovim#helper#feature(cur_text, complete_str) abort
if !has_key(s:internal_candidates_list, 'features')
let s:internal_candidates_list.features = s:make_cache_features()
endif
return copy(s:internal_candidates_list.features)
endfunction
function! necovim#helper#filetype(cur_text, complete_str) abort
if !has_key(s:internal_candidates_list, 'filetypes')
let s:internal_candidates_list.filetypes =
\ s:make_completion_list(map(
\ split(globpath(&runtimepath, 'syntax/*.vim'), '\n') +
\ split(globpath(&runtimepath, 'indent/*.vim'), '\n') +
\ split(globpath(&runtimepath, 'ftplugin/*.vim'), '\n')
\ , "matchstr(fnamemodify(v:val, ':t:r'), '^[[:alnum:]-]*')"))
endif
return copy(s:internal_candidates_list.filetypes)
endfunction
function! necovim#helper#function(cur_text, complete_str) abort
" Make cache.
if s:check_global_candidates('functions')
let s:global_candidates_list.functions = s:get_functionlist()
endif
if !has_key(s:internal_candidates_list, 'functions')
let s:internal_candidates_list.functions = s:make_cache_functions()
endif
let script_functions = values(s:get_cached_script_candidates().functions)
if a:complete_str =~ '^s:'
let list = script_functions
elseif a:complete_str =~ '^\a:'
let list = deepcopy(script_functions)
for keyword in list
let keyword.word = '<SID>' . keyword.word[2:]
let keyword.abbr = '<SID>' . keyword.abbr[2:]
endfor
else
let list = copy(s:internal_candidates_list.functions)
\ + copy(s:global_candidates_list.functions)
\ + script_functions
for functions in map(values(s:script_candidates_list), 'v:val.functions')
let list += values(filter(copy(functions), 'v:val.word[:1] !=# "s:"'))
endfor
endif
return list
endfunction
function! necovim#helper#let(cur_text, complete_str) abort
if a:cur_text !~ '='
return necovim#helper#var(a:cur_text, a:complete_str)
elseif a:cur_text =~# '\<let\s\+&\%([lg]:\)\?filetype\s*=\s*'
" FileType.
return necovim#helper#filetype(a:cur_text, a:complete_str)
else
return necovim#helper#expression(a:cur_text, a:complete_str)
endif
endfunction
function! necovim#helper#option(cur_text, complete_str) abort
" Make cache.
if !has_key(s:internal_candidates_list, 'options')
let s:internal_candidates_list.options = s:make_cache_options()
endif
if a:cur_text =~ '\<set\%[local]\s\+\%(filetype\|ft\)='
return necovim#helper#filetype(a:cur_text, a:complete_str)
else
return copy(s:internal_candidates_list.options)
endif
endfunction
function! necovim#helper#var_dictionary(cur_text, complete_str) abort
let var_name = matchstr(a:cur_text,
\'\%(\a:\)\?\h\w*\ze\.\%(\h\w*\%(()\?\)\?\)\?$')
let list = []
if a:cur_text =~ '[btwg]:\h\w*\.\%(\h\w*\%(()\?\)\?\)\?$'
let list = has_key(s:global_candidates_list.dictionary_variables, var_name) ?
\ values(s:global_candidates_list.dictionary_variables[var_name]) : []
elseif a:cur_text =~ 's:\h\w*\.\%(\h\w*\%(()\?\)\?\)\?$'
let list = values(get(s:get_cached_script_candidates().dictionary_variables,
\ var_name, {}))
endif
return list
endfunction
function! necovim#helper#var(cur_text, complete_str) abort
" Make cache.
if s:check_global_candidates('variables')
let s:global_candidates_list.variables =
\ s:get_variablelist(g:, 'g:') + s:get_variablelist(v:, 'v:')
\ + s:make_completion_list(['v:val'])
endif
if a:complete_str =~ '^[swtb]:'
let list = values(s:get_cached_script_candidates().variables)
if a:complete_str !~ '^s:'
let prefix = matchstr(a:complete_str, '^[swtb]:')
let list += s:get_variablelist(eval(prefix), prefix)
endif
elseif a:complete_str =~ '^[vg]:'
let list = copy(s:global_candidates_list.variables)
else
let list = s:get_local_variables()
endif
return list
endfunction
function! s:get_local_variables() abort
" Get local variable list.
let keyword_dict = {}
" Search function.
let line_num = line('.') - 1
let end_line = (line('.') > 100) ? line('.') - 100 : 1
while line_num >= end_line
let line = getline(line_num)
if line =~ '\<endf\%[unction]\>'
break
elseif line =~ '\<fu\%[nction]!\?\s\+'
" Get function arguments.
call s:analyze_variable_line(line, keyword_dict)
break
endif
let line_num -= 1
endwhile
let line_num += 1
let end_line = line('.') - 1
while line_num <= end_line
let line = getline(line_num)
if line =~ '\<\%(let\|for\)\s\+'
if line =~ '\<\%(let\|for\)\s\+s:' &&
\ has_key(s:script_candidates_list, bufnr('%'))
\ && has_key(s:script_candidates_list[bufnr('%')], 'variables')
let candidates_list = s:script_candidates_list[bufnr('%')].variables
else
let candidates_list = keyword_dict
endif
call s:analyze_variable_line(line, candidates_list)
endif
let line_num += 1
endwhile
return values(keyword_dict)
endfunction
function! s:get_cached_script_candidates() abort
return has_key(s:script_candidates_list, bufnr('%')) ?
\ s:script_candidates_list[bufnr('%')] : {
\ 'functions' : {}, 'variables' : {},
\ 'function_prototypes' : {}, 'dictionary_variables' : {} }
endfunction
function! s:get_script_candidates(bufnumber) abort
" Get script candidate list.
let function_dict = {}
let variable_dict = {}
let dictionary_variable_dict = {}
let function_prototypes = {}
let var_pattern = '\a:[[:alnum:]_:]*\.\h\w*\%(()\?\)\?'
for line in getbufline(a:bufnumber, 1, '$')
if line =~ '\<fu\%[nction]!\?\s\+'
call s:analyze_function_line(
\ line, function_dict, function_prototypes)
elseif line =~ '\<let\s\+'
" Get script variable.
call s:analyze_variable_line(line, variable_dict)
elseif line =~ var_pattern
while line =~ var_pattern
let var_name = matchstr(line, '\a:[[:alnum:]_:]*\ze\.\h\w*')
let candidates_dict = dictionary_variable_dict
if !has_key(candidates_dict, var_name)
let candidates_dict[var_name] = {}
endif
call s:analyze_dictionary_variable_line(
\ line, candidates_dict[var_name], var_name)
let line = line[matchend(line, var_pattern) :]
endwhile
endif
endfor
return { 'functions' : function_dict, 'variables' : variable_dict,
\ 'function_prototypes' : function_prototypes,
\ 'dictionary_variables' : dictionary_variable_dict }
endfunction
function! s:make_cache_options() abort
let options = map(filter(split(s:redir('set all'), '\s\{2,}\|\n')[1:],
\ "!empty(v:val) && v:val =~ '^\\h\\w*=\\?'"),
\ "substitute(v:val, '^no\\|=\\zs.*$', '', '')")
for option in copy(options)
if option[-1:] != '='
call add(options, 'no'.option)
endif
endfor
return map(filter(options, "v:val =~ '^\\h\\w*=\\?'"), "{
\ 'word' : substitute(v:val, '=$', '', ''), 'kind' : 'o',
\ }")
endfunction
function! s:make_cache_features() abort
let helpfile = expand(findfile('doc/eval.txt', &runtimepath))
if !filereadable(helpfile)
return []
endif
let features = []
let lines = readfile(helpfile)
let start = match(lines,
\ ((v:version > 704 || v:version == 704 && has('patch11')) ?
\ 'acl' : '^all_builtin_terms'))
let end = match(lines, '^x11')
for l in lines[start : end]
let _ = matchlist(l, '^\(\k\+\)\t\+\(.\+\)$')
if !empty(_)
call add(features, {
\ 'word' : _[1],
\ 'menu' : '; ' . _[2],
\ })
endif
endfor
call add(features, {
\ 'word' : 'patch',
\ 'menu' : '; Included patches Ex: patch123',
\ })
if has('patch-7.4.237')
call add(features, {
\ 'word' : 'patch-',
\ 'menu' : '; Version and patches Ex: patch-7.4.237'
\ })
endif
return features
endfunction
function! s:make_cache_functions() abort
let helpfile = expand(findfile('doc/eval.txt', &runtimepath))
if !filereadable(helpfile)
return []
endif
let lines = readfile(helpfile)
let functions = []
let start = match(lines, '^abs')
let end = match(lines, '^abs', start, 2)
for i in range(end-1, start, -1)
let func = matchstr(lines[i], '^\s*\zs\w\+(.\{-})')
if func != ''
call insert(functions, {
\ 'word' : substitute(func, '(\zs.\+)', '', ''),
\ 'abbr' : substitute(func, '(\zs\s\+', '', ''),
\ })
endif
endfor
return functions
endfunction
function! s:make_cache_commands() abort
let helpfile = expand(findfile('doc/index.txt', &runtimepath))
if !filereadable(helpfile)
return []
endif
let lines = readfile(helpfile)
let commands = []
let start = match(lines, '^|:!|')
let end = match(lines, '^|:\~|', start)
for lnum in range(end, start, -1)
let desc = substitute(lines[lnum], '^\s\+\ze', '', 'g')
let _ = matchlist(desc, '^|:\(.\{-}\)|\s\+\S\+')
if !empty(_)
call add(commands, {
\ 'word' : _[1], 'kind' : 'c',
\ })
endif
endfor
return commands
endfunction
function! s:make_cache_autocmds() abort
let helpfile = expand(findfile('doc/autocmd.txt', &runtimepath))
if !filereadable(helpfile)
return []
endif
let lines = readfile(helpfile)
let autocmds = []
let start = match(lines, '^|BufNewFile|')
let end = match(lines, '^|User|', start)
let desc = ''
for lnum in range(end, start, -1)
let desc = substitute(lines[lnum], '^\s\+\ze', '', 'g') . ' ' . desc
let _ = matchlist(desc, '^|\(.\{-}\)|\s\+\S\+')
if !empty(_)
call add(autocmds, { 'word' : _[1], })
let desc = ''
endif
endfor
return autocmds
endfunction
function! s:get_cmdlist() abort
let list = exists('*getcompletion') ?
\ getcompletion('', 'command') :
\ split(s:redir('command'), '\n')[1:]
return s:make_completion_list(list)
endfunction
function! s:get_variablelist(dict, prefix) abort
let kind_dict =
\ ['0', '""', '()', '[]', '{}', '.', 'b', 'no', 'j', 'ch']
return values(map(copy(a:dict), "{
\ 'word' : a:prefix.v:key,
\ 'kind' : kind_dict[type(v:val)],
\}"))
endfunction
function! s:get_functionlist() abort
let keyword_dict = {}
let function_prototypes = {}
for line in split(s:redir('function'), '\n')
let line = line[9:]
if line =~ '^<SNR>'
continue
endif
let orig_line = line
let word = matchstr(line, '\h[[:alnum:]_:#.]*()\?')
if word != ''
let keyword_dict[word] = {
\ 'word' : word, 'abbr' : line,
\}
let function_prototypes[word] = orig_line[len(word):]
endif
endfor
let s:global_candidates_list.function_prototypes = function_prototypes
return values(keyword_dict)
endfunction
function! s:get_augrouplist() abort
let list = exists('*getcompletion') ?
\ getcompletion('', 'augroup') :
\ split(s:redir('augroup') . ' END', '\s\|\n')
return s:make_completion_list(list)
endfunction
function! s:get_mappinglist() abort
let keyword_list = []
for line in split(s:redir('map'), '\n')
let map = matchstr(line, '^\a*\s*\zs\S\+')
if map !~ '^<' || map =~ '^<SNR>'
continue
endif
call add(keyword_list, { 'word' : map })
endfor
return keyword_list
endfunction
function! s:get_envlist() abort
let keyword_list = []
for line in split(system('set'), '\n')
let word = '$' . toupper(matchstr(line, '^\h\w*'))
call add(keyword_list, { 'word' : word, 'kind' : 'e' })
endfor
return keyword_list
endfunction
function! s:make_completion_list(list) abort
return map(copy(a:list), "{ 'word' : v:val }")
endfunction
function! s:analyze_function_line(line, keyword_dict, prototype) abort
" Get script function.
let line = substitute(matchstr(a:line,
\ '\<fu\%[nction]!\?\s\+\zs.*)'), '".*$', '', '')
let orig_line = line
let word = matchstr(line, '^\h[[:alnum:]_:#.]*()\?')
if word != '' && !has_key(a:keyword_dict, word)
let a:keyword_dict[word] = {
\ 'word' : word, 'abbr' : line, 'kind' : 'f'
\}
let a:prototype[word] = orig_line[len(word):]
endif
endfunction
function! s:analyze_variable_line(line, keyword_dict) abort
if a:line =~ '\<\%(let\|for\)\s\+\a[[:alnum:]_:]*'
" let var = pattern.
let word = matchstr(a:line, '\<\%(let\|for\)\s\+\zs\a[[:alnum:]_:]*')
let expression = matchstr(a:line, '\<let\s\+\a[[:alnum:]_:]*\s*=\s*\zs.*$')
if !has_key(a:keyword_dict, word)
let a:keyword_dict[word] = {
\ 'word' : word,
\ 'kind' : s:get_variable_type(expression)
\}
elseif expression != '' && a:keyword_dict[word].kind == ''
" Update kind.
let a:keyword_dict[word].kind = s:get_variable_type(expression)
endif
elseif a:line =~ '\<\%(let\|for\)\s\+\[.\{-}\]'
" let [var1, var2] = pattern.
let words = split(matchstr(a:line,
\'\<\%(let\|for\)\s\+\[\zs.\{-}\ze\]'), '[,[:space:]]\+')
let expressions = split(matchstr(a:line,
\'\<let\s\+\[.\{-}\]\s*=\s*\[\zs.\{-}\ze\]$'), '[,[:space:];]\+')
let i = 0
while i < len(words)
let expression = get(expressions, i, '')
let word = words[i]
if !has_key(a:keyword_dict, word)
let a:keyword_dict[word] = {
\ 'word' : word,
\ 'kind' : s:get_variable_type(expression)
\}
elseif expression != '' && a:keyword_dict[word].kind == ''
" Update kind.
let a:keyword_dict[word].kind = s:get_variable_type(expression)
endif
let i += 1
endwhile
elseif a:line =~ '\<fu\%[nction]!\?\s\+'
" Get function arguments.
for arg in split(matchstr(a:line, '^[^(]*(\zs[^)]*'), '\s*,\s*')
let word = 'a:' . (arg == '...' ? '000' : arg)
let a:keyword_dict[word] = {
\ 'word' : word,
\ 'kind' : (arg == '...' ? '[]' : '')
\}
endfor
if a:line =~ '\.\.\.)'
" Extra arguments.
for arg in range(5)
let word = 'a:' . arg
let a:keyword_dict[word] = {
\ 'word' : word,
\ 'kind' : (arg == 0 ? '0' : '')
\}
endfor
endif
endif
endfunction
function! s:analyze_dictionary_variable_line(line, keyword_dict, var_name) abort
let let_pattern = '\<let\s\+'.a:var_name.'\.\h\w*'
let call_pattern = '\<call\s\+'.a:var_name.'\.\h\w*()\?'
if a:line =~ let_pattern
let word = matchstr(a:line, a:var_name.'\zs\.\h\w*')
let kind = ''
elseif a:line =~ call_pattern
let word = matchstr(a:line, a:var_name.'\zs\.\h\w*()\?')
let kind = '()'
else
let word = matchstr(a:line, a:var_name.'\zs.\h\w*\%(()\?\)\?')
let kind = s:get_variable_type(
\ matchstr(a:line, a:var_name.'\.\h\w*\zs.*$'))
endif
if !has_key(a:keyword_dict, word)
let a:keyword_dict[word] = { 'word' : word, 'kind' : kind }
elseif kind != '' && a:keyword_dict[word].kind == ''
" Update kind.
let a:keyword_dict[word].kind = kind
endif
endfunction
function! s:split_args(cur_text, complete_str) abort
let args = split(a:cur_text)
if a:complete_str == ''
call add(args, '')
endif
return args
endfunction
" Initialize return types.
function! s:set_dictionary_helper(variable, keys, value) abort
for key in split(a:keys, ',')
let a:variable[key] = a:value
endfor
endfunction
let s:function_return_types = {}
call s:set_dictionary_helper(
\ s:function_return_types,
\ 'len,match,matchend',
\ '0')
call s:set_dictionary_helper(
\ s:function_return_types,
\ 'input,matchstr',
\ '""')
call s:set_dictionary_helper(
\ s:function_return_types,
\ 'expand,filter,sort,split',
\ '[]')
function! s:get_variable_type(expression) abort
" Analyze variable type.
if a:expression =~ '^\%(\s*+\)\?\s*\d\+\.\d\+'
return '.'
elseif a:expression =~ '^\%(\s*+\)\?\s*\d\+'
return '0'
elseif a:expression =~ '^\%(\s*\.\)\?\s*["'']'
return '""'
elseif a:expression =~ '\<function('
return '()'
elseif a:expression =~ '^\%(\s*+\)\?\s*\['
return '[]'
elseif a:expression =~ '^\s*{\|^\.\h[[:alnum:]_:]*'
return '{}'
elseif a:expression =~ '\<\h\w*('
" Function.
let func_name = matchstr(a:expression, '\<\zs\h\w*\ze(')
return has_key(s:function_return_types, func_name) ? s:function_return_types[func_name] : ''
else
return ''
endif
endfunction
function! s:set_dictionary_helper(variable, keys, pattern) abort
for key in split(a:keys, '\s*,\s*')
if !has_key(a:variable, key)
let a:variable[key] = a:pattern
endif
endfor
endfunction
function! s:check_global_candidates(key) abort
if s:global_candidates_list.runtimepath !=# &runtimepath
let s:global_candidates_list.runtimepath = &runtimepath
return 1
endif
return !has_key(s:global_candidates_list, a:key)
endfunction
function! s:redir(command) abort
if exists('*execute')
return execute(a:command)
endif
redir => r
execute 'silent!' a:command
redir END
return r
endfunction
function! s:has_cmdline() abort
if !exists('*getcompletion')
return 0
endif
try
call getcompletion('', 'cmdline')
catch
return 0
endtry
return 1
endfunction
" Removes duplicates from a list.
function! s:uniq(list) abort
return s:uniq_by(a:list, 'v:val')
endfunction
" Removes duplicates from a list.
function! s:uniq_by(list, f) abort
let list = map(copy(a:list), printf('[v:val, %s]', a:f))
let i = 0
let seen = {}
while i < len(list)
let key = string(list[i][1])
if has_key(seen, key)
call remove(list, i)
else
let seen[key] = 1
let i += 1
endif
endwhile
return map(list, 'v:val[0]')
endfunction