Skip to content

Commit 17735fb

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 17735fb

File tree

8 files changed

+180
-13
lines changed

8 files changed

+180
-13
lines changed

autoload/go/package.vim

Lines changed: 111 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,58 @@ 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+
while l:dir != l:root
95+
let l:vendordir = l:dir . l:vendorsuffix
96+
if isdirectory(l:vendordir)
97+
let l:vendordirs = add(l:vendordirs, l:vendordir)
98+
endif
99+
100+
let l:dir = fnamemodify(l:dir, ':h')
101+
endwhile
102+
103+
return l:vendordirs
104+
endif
105+
106+
let l:vendordir = l:module.dir . l:vendorsuffix
107+
if !isdirectory(l:vendordir)
108+
return []
109+
endif
110+
return [l:vendordir]
111+
endfunction
112+
61113
let s:import_paths = {}
62114
" ImportPath returns the import path of the package for current buffer.
63115
function! go#package#ImportPath() abort
@@ -144,33 +196,80 @@ function! go#package#Complete(ArgLead, CmdLine, CursorPos) abort
144196
return go#package#CompleteMembers(words[1], words[2])
145197
endif
146198

147-
let dirs = go#package#Paths()
199+
let dirs = s:paths()
200+
let module = s:module()
148201

149-
if len(dirs) == 0
202+
if len(dirs) == 0 && empty(module)
150203
" should not happen
151204
return []
152205
endif
153206

207+
let vendordirs = s:vendordirs()
208+
154209
let ret = {}
155210
for dir in dirs
156211
" this may expand to multiple lines
157212
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$'
213+
let root = add(root, expand(dir . '/src'), )
214+
let root = extend(root, vendordirs)
215+
let root = add(root, module)
216+
for item in root
217+
" item may be a dictionary when operating in a module.
218+
if type(item) == type({})
219+
if empty(item)
164220
continue
165221
endif
166-
let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'),
222+
let dir = item.dir
223+
let path = item.path
224+
else
225+
let dir = item
226+
let path = item
227+
endif
228+
229+
if !empty(module) && dir ==# module.dir
230+
if stridx(a:ArgLead, module.path) == 0
231+
if len(a:ArgLead) != len(module.path)
232+
let glob = globpath(module.dir, substitute(a:ArgLead, module.path . '/\?', '', '').'*')
233+
else
234+
let glob = module.dir
235+
endif
236+
elseif stridx(module.path, a:ArgLead) == 0 && stridx(module.path, '/', len(a:ArgLead)) < 0
237+
" use the module directory when a:ArgLead is contained in
238+
" module.path and module.path does not have any path segments after
239+
" a:ArgLead.
240+
let glob = module.dir
241+
else
242+
continue
243+
endif
244+
else
245+
let glob = globpath(dir, a:ArgLead.'*')
246+
endif
247+
for candidate in split(glob)
248+
if isdirectory(candidate)
249+
" TODO(bc): use wildignore instead of filtering out vendor
250+
" directories manually?
251+
if fnamemodify(candidate, ':t') == 'vendor'
252+
continue
253+
endif
254+
let candidate .= '/'
255+
elseif candidate !~ '\.a$'
256+
continue
257+
endif
258+
259+
if dir !=# path
260+
let candidate = substitute(candidate, '^' . dir, path, 'g')
261+
else
262+
let candidate = candidate[len(dir)+1:]
263+
endif
264+
" replace a backslash with a forward slash and drop .a suffixes
265+
let candidate = substitute(substitute(candidate, '[\\]', '/', 'g'),
167266
\ '\.a$', '', 'g')
168267

169268
" without this the result can have duplicates in form of
170269
" 'encoding/json' and '/encoding/json/'
171-
let i = go#util#StripPathSep(i)
270+
let candidate = go#util#StripPathSep(candidate)
172271

173-
let ret[i] = i
272+
let ret[candidate] = candidate
174273
endfor
175274
endfor
176275
endfor

autoload/go/package_test.vim

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
" don't spam the user when Vim is started in Vi compatibility mode
2+
let s:cpo_save = &cpo
3+
set cpo&vim
4+
5+
func! Test_Complete_GOPATH_simple() abort
6+
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
7+
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
8+
call s:complete('package', ['package'])
9+
endfunc
10+
11+
func! Test_Complete_Module_simple() abort
12+
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
13+
call s:complete('package', ['package'])
14+
endfunc
15+
16+
func! Test_Complete_GOPATH_subdirs() abort
17+
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
18+
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
19+
call s:complete('package/', ['package/bar', 'package/baz'])
20+
endfunc
21+
22+
func! Test_Complete_Module_subdirs() abort
23+
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
24+
call s:complete('package/', ['package/bar', 'package/baz'])
25+
endfunc
26+
27+
func! Test_Complete_GOPATH_baronly() abort
28+
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
29+
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
30+
call s:complete('package/bar', ['package/bar'])
31+
endfunc
32+
33+
func! Test_Complete_Module_baronly() abort
34+
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
35+
call s:complete('package/bar', ['package/bar'])
36+
endfunc
37+
38+
func! Test_Complete_GOPATH_vendor() abort
39+
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package'
40+
silent exe 'edit ' . $GOPATH . '/src/package/package.go'
41+
call s:complete('foo', ['foo'])
42+
endfunc
43+
44+
func! Test_Complete_Module_vendor() abort
45+
silent exe 'edit ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/package/src/package/package.go'
46+
call s:complete('foo', ['foo'])
47+
endfunc
48+
49+
func! s:complete(arglead, expected) abort
50+
let l:candidates = go#package#Complete(a:arglead, '', 1)
51+
call assert_equal(a:expected, l:candidates)
52+
endfunc
53+
54+
" restore Vi compatibility settings
55+
let &cpo = s:cpo_save
56+
unlet s:cpo_save
57+
58+
" vim: sw=2 ts=2 et

autoload/go/test-fixtures/package/src/package/bar/.gitkeep

Whitespace-only changes.

autoload/go/test-fixtures/package/src/package/baz/.gitkeep

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module package
2+
3+
go 1.12
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package main
2+
3+
import "fmt"
4+
5+
func main() {
6+
fmt.Println("vim-go")
7+
}

autoload/go/test-fixtures/package/src/package/vendor/foo/.gitkeep

Whitespace-only changes.

scripts/run-vim

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ fi
2323

2424
dir="/tmp/vim-go-test/$1-install"
2525
export GOPATH=$dir
26-
export GO111MODULE=off
26+
export GO111MODULE=auto
2727
export PATH=${GOPATH}/bin:$PATH
2828
shift
2929

0 commit comments

Comments
 (0)