|
scriptencoding utf-8 |
|
|
|
let s:cache = {} |
|
let s:cache_enable = 1 |
|
|
|
function! s:get_cache(key, ttl, valueFn) abort |
|
if !s:cache_enable |
|
return a:valueFn(a:key) |
|
endif |
|
let l:now = localtime() |
|
let l:entry = get(s:cache, a:key, {}) |
|
if len(l:entry) >= 2 && l:entry['expiresAt'] > l:now |
|
return l:entry['value']->copy() |
|
endif |
|
" get new value for a key. |
|
let l:entry = { 'value': a:valueFn(a:key), 'expiresAt': l:now + a:ttl } |
|
let s:cache[a:key] = l:entry |
|
return l:entry['value']->copy() |
|
endfunction |
|
|
|
" gocomplete#_cache returns a dictionary of complement cache. |
|
function! gocomplete#_cache() abort |
|
return s:cache |
|
endfunction |
|
|
|
" gocomplete#_cache_reset resets complement cache. |
|
function! gocomplete#_cache_reset() abort |
|
let s:cache = {} |
|
endfunction |
|
|
|
" gocomplete#_cache_enable |
|
function! gocomplete#_cache_enable(v) abort |
|
let s:cache_enable = a:v |
|
endfunction |
|
|
|
function! s:goroot() abort |
|
return s:get_cache('ENV:GOROOT', 3600, {_ -> systemlist('go env GOROOT')[0]}) |
|
endfunction |
|
|
|
function! s:system_jsonl(cmd) abort |
|
let l:out = systemlist(a:cmd) |
|
let l:out = map(l:out, {_, line -> line == '}' ? line .. ',' : line}) |
|
let l:out = join(l:out) |
|
let l:out = json_decode('[' .. l:out .. ']') |
|
return l:out |
|
endfunction |
|
|
|
function! s:match_module(modpath, lead) abort |
|
if len(a:lead) > len(a:modpath) |
|
return stridx(a:lead, a:modpath .. '/') == 0 |
|
endif |
|
return stridx(a:modpath, a:lead) == 0 |
|
endfunction |
|
|
|
function! s:match_package(pkgpath, lead) abort |
|
return stridx(a:pkgpath, a:lead) == 0 |
|
endfunction |
|
|
|
function! s:filter_set(set, pat, fn) abort |
|
let l:list = copy(a:set) |
|
let l:list = map(l:list, {_, v -> a:fn(v)}) |
|
let l:list = map(l:list, {_, v -> matchstr(v, a:pat)}) |
|
let l:list = filter(l:list, {_, v -> v != ''}) |
|
let l:list = sort(l:list) |
|
let l:list = uniq(l:list) |
|
return l:list |
|
endfunction |
|
|
|
function! s:filter_modules(mods, pat) abort |
|
return s:filter_set(a:mods, a:pat, {m -> m.Path}) |
|
endfunction |
|
|
|
function! s:filter_packages(pkgs, pat) abort |
|
return s:filter_set(a:pkgs, a:pat, {p -> p.ImportPath}) |
|
endfunction |
|
|
|
" gocomplete#list_packages obtains all packages in the module. the module is |
|
" specified by its path. |
|
function! gocomplete#list_packages(modpath = '', modver = '') abort |
|
if a:modpath != '' |
|
let l:key = 'PACKAGES:' .. a:modpath .. (a:modver != '' ? '@' .. a:modver : '') |
|
let l:ttl = a:modver != '' ? 3600 : 300 |
|
return s:get_cache(l:key, l:ttl, {_ -> s:system_jsonl('go list -find -json ' .. a:modpath .. '/...')}) |
|
endif |
|
" list standard packages. |
|
return s:get_cache('STANDARD_PACKAGES', 86400, {_ -> s:system_jsonl('go list -find -json std')}) |
|
endfunction |
|
|
|
" gocomplete#list_modules obtains all modules which the project depends on. |
|
" the project is specified by directory, default is current directory. |
|
function! gocomplete#list_modules(dir = '') abort |
|
let l:olddir = '' |
|
if a:dir != '' |
|
let l:olddir = chdir(a:dir) |
|
endif |
|
try |
|
return s:get_cache('MODULES:' .. a:dir, 900, {_ -> s:system_jsonl('go list -m -json all')}) |
|
finally |
|
if l:olddir != '' |
|
call chdir(l:olddir) |
|
endif |
|
endtry |
|
endfunction |
|
|
|
" gocomplete#find_modules finds and returns modules which match with a lead. |
|
function! gocomplete#find_modules(lead) abort |
|
let l:mods = gocomplete#list_modules(getcwd()) |
|
return l:mods->filter({_, m -> s:match_module(m.Path, a:lead)}) |
|
endfunction |
|
|
|
" gocomplete#find_packages finds and returns packages which match with a lead |
|
" in a module. |
|
function! gocomplete#find_packages(lead, modpath, modver = '') abort |
|
let l:pkgs = gocomplete#list_packages(a:modpath, a:modver) |
|
return l:pkgs->filter({_, p -> s:match_package(p.ImportPath, a:lead)}) |
|
endfunction |
|
|
|
" gocomplete#packages completes packages with a lead. it can be used in Vim's |
|
" command completion. |
|
function! gocomplete#packages(lead, line = 0, pos = 0) abort |
|
let l:mods = gocomplete#find_modules(a:lead) |
|
if len(l:mods) == 0 |
|
return [] |
|
elseif len(l:mods) > 1 |
|
let l:list = s:filter_modules(l:mods, a:lead .. '[^/]*') |
|
if len(l:list) > 1 |
|
return l:list |
|
endif |
|
return s:filter_modules(l:mods, a:lead .. '[^/]*\%(/[^/]*\)\?') |
|
endif |
|
" case: len(l:mods) == 1, enumerate packages in the module. |
|
let [l:modpath, l:modver] = [l:mods[0].Path, get(l:mods[0], 'Version', '')] |
|
let l:pkgs = gocomplete#find_packages(a:lead, l:modpath, l:modver) |
|
if len(l:pkgs) == 0 |
|
return [a:lead, l:modpath] |
|
endif |
|
let l:pat = len(a:lead) > len(l:modpath) ? a:lead : l:modpath |
|
let l:list = s:filter_packages(l:pkgs, l:pat .. '[^/]*') |
|
if len(l:list) == 0 |
|
return [a:lead, l:modpath] |
|
elseif len(l:list) > 1 |
|
return l:list |
|
endif |
|
" case: candidate is just only a package, check its sub packages. |
|
return s:filter_packages(l:pkgs, l:pat .. '[^/]*\%(/[^/]*\)\?') |
|
endfunction |