Skip to content

Commit dcbfdf3

Browse files
authored
Merge pull request #1697 from bhcleek/async-info
get info from gocode asynchronously
2 parents 945b636 + 2499155 commit dcbfdf3

File tree

2 files changed

+166
-77
lines changed

2 files changed

+166
-77
lines changed

autoload/go/complete.vim

Lines changed: 165 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,51 @@
11
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
22

3-
function! s:gocodeCurrentBuffer() abort
4-
let file = tempname()
5-
call writefile(go#util#GetLines(), file)
6-
return file
7-
endfunction
8-
9-
function! s:gocodeCommand(cmd, preargs, args) abort
10-
for i in range(0, len(a:args) - 1)
11-
let a:args[i] = go#util#Shellescape(a:args[i])
12-
endfor
13-
for i in range(0, len(a:preargs) - 1)
14-
let a:preargs[i] = go#util#Shellescape(a:preargs[i])
15-
endfor
16-
3+
function! s:gocodeCommand(cmd, args) abort
174
let bin_path = go#path#CheckBinPath("gocode")
185
if empty(bin_path)
19-
return
6+
return []
207
endif
218

9+
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
10+
11+
let cmd = [bin_path]
12+
let cmd = extend(cmd, ['-sock', socket_type])
13+
let cmd = extend(cmd, ['-f', 'vim'])
14+
let cmd = extend(cmd, [a:cmd])
15+
let cmd = extend(cmd, a:args)
16+
17+
return cmd
18+
endfunction
19+
20+
function! s:sync_gocode(cmd, args, input) abort
2221
" We might hit cache problems, as gocode doesn't handle different GOPATHs
2322
" well. See: https://github.com/nsf/gocode/issues/239
2423
let old_goroot = $GOROOT
2524
let $GOROOT = go#util#env("goroot")
2625

2726
try
28-
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
29-
let cmd = printf('%s -sock %s %s %s %s',
30-
\ go#util#Shellescape(bin_path),
31-
\ socket_type,
32-
\ join(a:preargs),
33-
\ go#util#Shellescape(a:cmd),
34-
\ join(a:args)
35-
\ )
36-
27+
let cmd = s:gocodeCommand(a:cmd, a:args)
3728
" gocode can sometimes be slow, so redraw now to avoid waiting for gocode
3829
" to return before redrawing automatically.
3930
redraw
4031

41-
let result = go#util#System(cmd)
32+
let [l:result, l:err] = go#util#Exec(cmd, a:input)
4233
finally
4334
let $GOROOT = old_goroot
4435
endtry
4536

46-
if go#util#ShellError() != 0
47-
return "[\"0\", []]"
48-
else
49-
if &encoding != 'utf-8'
50-
let result = iconv(result, 'utf-8', &encoding)
51-
endif
52-
return result
37+
if l:err != 0
38+
return "[0, []]"
39+
endif
40+
41+
if &encoding != 'utf-8'
42+
let l:result = iconv(l:result, 'utf-8', &encoding)
5343
endif
54-
endfunction
5544

56-
function! s:gocodeCurrentBufferOpt(filename) abort
57-
return '-in=' . a:filename
45+
return l:result
5846
endfunction
5947

48+
" TODO(bc): reset when gocode isn't running
6049
let s:optionsEnabled = 0
6150
function! s:gocodeEnableOptions() abort
6251
if s:optionsEnabled
@@ -82,62 +71,161 @@ endfunction
8271
function! s:gocodeAutocomplete() abort
8372
call s:gocodeEnableOptions()
8473

85-
let filename = s:gocodeCurrentBuffer()
86-
let result = s:gocodeCommand('autocomplete',
87-
\ [s:gocodeCurrentBufferOpt(filename), '-f=vim'],
88-
\ [expand('%:p'), go#util#OffsetCursor()])
89-
call delete(filename)
90-
return result
74+
" use the offset as is, because the cursor position is the position for
75+
" which autocomplete candidates are needed.
76+
return s:sync_gocode('autocomplete',
77+
\ [expand('%:p'), go#util#OffsetCursor()],
78+
\ go#util#GetLines())
9179
endfunction
9280

81+
" go#complete#GoInfo returns the description of the identifier under the
82+
" cursor.
9383
function! go#complete#GetInfo() abort
84+
return s:sync_info(0)
85+
endfunction
86+
87+
function! go#complete#Info(auto) abort
88+
if go#util#has_job()
89+
return s:async_info(a:auto)
90+
else
91+
return s:sync_info(a:auto)
92+
endif
93+
endfunction
94+
95+
function! s:async_info(auto)
96+
if exists("s:async_info_job")
97+
call job_stop(s:async_info_job)
98+
unlet s:async_info_job
99+
endif
100+
101+
let state = {
102+
\ 'exited': 0,
103+
\ 'exit_status': 0,
104+
\ 'closed': 0,
105+
\ 'messages': [],
106+
\ 'auto': a:auto
107+
\ }
108+
109+
function! s:callback(chan, msg) dict
110+
let l:msg = a:msg
111+
if &encoding != 'utf-8'
112+
let l:msg = iconv(l:msg, 'utf-8', &encoding)
113+
endif
114+
call add(self.messages, l:msg)
115+
endfunction
116+
117+
function! s:exit_cb(job, exitval) dict
118+
let self.exit_status = a:exitval
119+
let self.exited = 1
120+
121+
if self.closed
122+
call self.complete()
123+
endif
124+
endfunction
125+
126+
function! s:close_cb(ch) dict
127+
let self.closed = 1
128+
if self.exited
129+
call self.complete()
130+
endif
131+
endfunction
132+
133+
function state.complete() dict
134+
if self.exit_status != 0
135+
return
136+
endif
137+
138+
let result = s:info_filter(self.auto, join(self.messages, "\n"))
139+
call s:info_complete(self.auto, result)
140+
endfunction
141+
142+
" add 1 to the offset, so that the position at the cursor will be included
143+
" in gocode's search
94144
let offset = go#util#OffsetCursor()+1
95-
let filename = s:gocodeCurrentBuffer()
96-
let result = s:gocodeCommand('autocomplete',
97-
\ [s:gocodeCurrentBufferOpt(filename), '-f=godit'],
145+
146+
" We might hit cache problems, as gocode doesn't handle different GOPATHs
147+
" well. See: https://github.com/nsf/gocode/issues/239
148+
let env = {
149+
\ "GOROOT": go#util#env("goroot")
150+
\ }
151+
152+
let cmd = s:gocodeCommand('autocomplete',
98153
\ [expand('%:p'), offset])
99-
call delete(filename)
100154

101-
" first line is: Charcount,,NumberOfCandidates, i.e: 8,,1
102-
" following lines are candiates, i.e: func foo(name string),,foo(
103-
let out = split(result, '\n')
155+
" TODO(bc): Don't write the buffer to a file; pass the buffer directrly to
156+
" gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
157+
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
158+
" should work.
159+
let options = {
160+
\ 'env': env,
161+
\ 'in_io': 'file',
162+
\ 'in_name': s:gocodeFile(),
163+
\ 'callback': funcref("s:callback", [], state),
164+
\ 'exit_cb': funcref("s:exit_cb", [], state),
165+
\ 'close_cb': funcref("s:close_cb", [], state)
166+
\ }
104167

105-
" no candidates are found
106-
if len(out) == 1
168+
let s:async_info_job = job_start(cmd, options)
169+
endfunction
170+
171+
function! s:gocodeFile()
172+
let file = tempname()
173+
call writefile(go#util#GetLines(), file)
174+
return file
175+
endfunction
176+
177+
function! s:sync_info(auto)
178+
" auto is true if we were called by g:go_auto_type_info's autocmd
179+
180+
" add 1 to the offset, so that the position at the cursor will be included
181+
" in gocode's search
182+
let offset = go#util#OffsetCursor()+1
183+
184+
let result = s:sync_gocode('autocomplete',
185+
\ [expand('%:p'), offset],
186+
\ go#util#GetLines())
187+
188+
let result = s:info_filter(a:auto, result)
189+
call s:info_complete(a:auto, result)
190+
endfunction
191+
192+
function! s:info_filter(auto, result) abort
193+
if empty(a:result)
107194
return ""
108195
endif
109196

110-
" only one candidate is found
111-
if len(out) == 2
112-
return split(out[1], ',,')[0]
197+
let l:result = eval(a:result)
198+
if len(l:result) != 2
199+
return ""
113200
endif
114201

115-
" to many candidates are available, pick one that maches the word under the
116-
" cursor
117-
let infos = []
118-
for info in out[1:]
119-
call add(infos, split(info, ',,')[0])
120-
endfor
202+
let l:candidates = l:result[1]
203+
if len(l:candidates) == 1
204+
" When gocode panics in vim mode, it returns
205+
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
206+
if a:auto && l:candidates[0].info ==# "PANIC PANIC PANIC"
207+
return ""
208+
endif
209+
210+
return l:candidates[0].info
211+
endif
121212

213+
let filtered = []
122214
let wordMatch = '\<' . expand("<cword>") . '\>'
123215
" escape single quotes in wordMatch before passing it to filter
124216
let wordMatch = substitute(wordMatch, "'", "''", "g")
125-
let filtered = filter(infos, "v:val =~ '".wordMatch."'")
217+
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
126218

127-
if len(filtered) == 1
128-
return filtered[0]
219+
if len(l:filtered) != 1
220+
return ""
129221
endif
130222

131-
return ""
223+
return l:filtered[0].info
132224
endfunction
133225

134-
function! go#complete#Info(auto) abort
135-
" auto is true if we were called by g:go_auto_type_info's autocmd
136-
let result = go#complete#GetInfo()
137-
if !empty(result)
138-
" if auto, and the result is a PANIC by gocode, hide it
139-
if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif
140-
echo "vim-go: " | echohl Function | echon result | echohl None
226+
function! s:info_complete(auto, result) abort
227+
if !empty(a:result)
228+
echo "vim-go: " | echohl Function | echon a:result | echohl None
141229
endif
142230
endfunction
143231

@@ -146,20 +234,22 @@ function! s:trim_bracket(val) abort
146234
return a:val
147235
endfunction
148236

237+
let s:completions = ""
149238
function! go#complete#Complete(findstart, base) abort
150239
"findstart = 1 when we need to get the text length
151240
if a:findstart == 1
152-
execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete()
153-
return col('.') - g:gocomplete_completions[0] - 1
241+
execute "silent let s:completions = " . s:gocodeAutocomplete()
242+
return col('.') - s:completions[0] - 1
154243
"findstart = 0 when we need to return the list of completions
155244
else
156245
let s = getline(".")[col('.') - 1]
157246
if s =~ '[(){}\{\}]'
158-
return map(copy(g:gocomplete_completions[1]), 's:trim_bracket(v:val)')
247+
return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
159248
endif
160-
return g:gocomplete_completions[1]
249+
250+
return s:completions[1]
161251
endif
162-
endf
252+
endfunction
163253

164254
function! go#complete#ToggleAutoTypeInfo() abort
165255
if get(g:, "go_auto_type_info", 0)
@@ -172,5 +262,4 @@ function! go#complete#ToggleAutoTypeInfo() abort
172262
call go#util#EchoProgress("auto type info enabled")
173263
endfunction
174264

175-
176265
" vim: sw=2 ts=2 et

doc/vim-go.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1102,7 +1102,7 @@ cleaned for each package after `60` seconds. This can be changed with the
11021102
*go#complete#GetInfo()*
11031103

11041104
Returns the description of the identifer under the cursor. Can be used to plug
1105-
into the statusline. This function is also used for |'g:go_auto_type_info'|.
1105+
into the statusline.
11061106

11071107
==============================================================================
11081108
SETTINGS *go-settings*

0 commit comments

Comments
 (0)