Skip to content

Commit e792d47

Browse files
committed
complete vendored packages and modules
Teach vim-go how to complete vendored package names and packages in modules. Fixes #1909 and fixes #2212
1 parent 686167f commit e792d47

File tree

1 file changed

+108
-12
lines changed

1 file changed

+108
-12
lines changed

autoload/go/package.vim

Lines changed: 108 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ if len(s:goarch) == 0
3232
endif
3333
endif
3434

35-
function! go#package#Paths() abort
35+
function! s:paths() abort
3636
let dirs = []
3737

3838
if !exists("s:goroot")
@@ -58,6 +58,64 @@ function! go#package#Paths() abort
5858
return dirs
5959
endfunction
6060

61+
function! s:module() abort
62+
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-m', '-f', '{{.Dir}}'])
63+
if l:err != 0
64+
return {}
65+
endif
66+
let l:dir = split(l:out, '\n')[0]
67+
68+
let [l:out, l:err] = go#util#ExecInDir(['go', 'list', '-m', '-f', '{{.Path}}'])
69+
if l:err != 0
70+
return {}
71+
endif
72+
let l:path = split(l:out, '\n')[0]
73+
74+
return {'dir': l:dir, 'path': l:path}
75+
endfunction
76+
77+
function! s:vendordirs() abort
78+
let l:vendorsuffix = go#util#PathSep() . 'vendor'
79+
let l:module = s:module()
80+
if empty(l:module)
81+
let [l:root, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Root}}'])
82+
if l:err != 0
83+
return []
84+
endif
85+
let l:root = split(l:root, '\n')[0] . go#util#PathSep() . 'src'
86+
87+
let [l:dir, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Dir}}'])
88+
if l:err != 0
89+
return []
90+
endif
91+
let l:dir = split(l:dir, '\n')[0]
92+
93+
let l:vendordirs = []
94+
let l:i = 0
95+
while l:dir != l:root
96+
let l:i += 1
97+
98+
if l:i == 4
99+
return []
100+
endif
101+
let l:vendordir = l:dir . l:vendorsuffix
102+
if isdirectory(l:vendordir)
103+
let l:vendordirs = add(l:vendordirs, l:vendordir)
104+
endif
105+
106+
let l:dir = fnamemodify(l:dir, ':h')
107+
endwhile
108+
109+
return l:vendordirs
110+
endif
111+
112+
let l:vendordir = l:module.dir . l:vendorsuffix
113+
if !isdirectory(l:vendordir)
114+
return []
115+
endif
116+
return [l:vendordir]
117+
endfunction
118+
61119
let s:import_paths = {}
62120
" ImportPath returns the import path of the package for current buffer.
63121
function! go#package#ImportPath() abort
@@ -144,33 +202,71 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
144202
return go#package#CompleteMembers(words[1], words[2])
145203
endif
146204

147-
let dirs = go#package#Paths()
205+
let dirs = s:paths()
206+
let module = s:module()
148207

149-
if len(dirs) == 0
208+
if len(dirs) == 0 && empty(module)
150209
" should not happen
151210
return []
152211
endif
153212

213+
let vendordirs = s:vendordirs()
214+
154215
let ret = {}
155216
for dir in dirs
156217
" this may expand to multiple lines
157218
let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
158-
call add(root, expand(dir . '/src'))
159-
for r in root
160-
for i in split(globpath(r, a:ArgLead.'*'), "\n")
161-
if isdirectory(i)
162-
let i .= '/'
163-
elseif i !~ '\.a$'
219+
let root = add(root, expand(dir . '/src'), )
220+
let root = extend(root, vendordirs)
221+
let root = add(root, module)
222+
for item in root
223+
" item may be a dictionary when operating in a module.
224+
if type(item) == type({})
225+
if empty(item)
226+
continue
227+
endif
228+
let dir = item.dir
229+
let path = item.path
230+
else
231+
let dir = item
232+
let path = item
233+
endif
234+
235+
if !empty(module) && dir == module.dir
236+
if stridx(a:ArgLead, module.path) == 0 && len(a:ArgLead) != len(module.path)
237+
let glob = globpath(module.dir, substitute(a:ArgLead, module.path, '', '').'*')
238+
elseif stridx(module.path, a:ArgLead) == 0 && stridx(module.path, '/', len(a:ArgLead)) < 0
239+
" use the module directory when a:ArgLead is contained in
240+
" module.path and module.path does not have any path segments after
241+
" a:ArgLead.
242+
let glob = module.dir
243+
else
244+
continue
245+
endif
246+
else
247+
let glob = globpath(dir, a:ArgLead.'*')
248+
endif
249+
for candidate in split(glob)
250+
if isdirectory(candidate)
251+
let candidate .= '/'
252+
elseif candidate !~ '\.a$'
164253
continue
165254
endif
166-
let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'),
255+
256+
if dir !=# path
257+
let candidate = substitute(candidate, '^' . dir, path, 'g')
258+
else
259+
let candidate = candidate[len(dir)+1:]
260+
endif
261+
" replace a backslash with a forward slash and drop .a suffixes
262+
let candidate = substitute(substitute(candidate, '[\\]', '/', 'g'),
167263
\ '\.a$', '', 'g')
168264

169265
" without this the result can have duplicates in form of
170266
" 'encoding/json' and '/encoding/json/'
171-
let i = go#util#StripPathSep(i)
267+
let candidate = go#util#StripPathSep(candidate)
172268

173-
let ret[i] = i
269+
let ret[candidate] = candidate
174270
endfor
175271
endfor
176272
endfor

0 commit comments

Comments
 (0)