complete.vim 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
  2. function! s:gocodeCommand(cmd, args) abort
  3. let bin_path = go#path#CheckBinPath("gocode")
  4. if empty(bin_path)
  5. return []
  6. endif
  7. let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
  8. let cmd = [bin_path]
  9. let cmd = extend(cmd, ['-sock', socket_type])
  10. let cmd = extend(cmd, ['-f', 'vim'])
  11. let cmd = extend(cmd, [a:cmd])
  12. let cmd = extend(cmd, a:args)
  13. return cmd
  14. endfunction
  15. function! s:sync_gocode(cmd, args, input) abort
  16. " We might hit cache problems, as gocode doesn't handle different GOPATHs
  17. " well. See: https://github.com/nsf/gocode/issues/239
  18. let old_goroot = $GOROOT
  19. let $GOROOT = go#util#env("goroot")
  20. try
  21. let cmd = s:gocodeCommand(a:cmd, a:args)
  22. " gocode can sometimes be slow, so redraw now to avoid waiting for gocode
  23. " to return before redrawing automatically.
  24. redraw
  25. let [l:result, l:err] = go#util#Exec(cmd, a:input)
  26. finally
  27. let $GOROOT = old_goroot
  28. endtry
  29. if l:err != 0
  30. return "[0, []]"
  31. endif
  32. if &encoding != 'utf-8'
  33. let l:result = iconv(l:result, 'utf-8', &encoding)
  34. endif
  35. return l:result
  36. endfunction
  37. " TODO(bc): reset when gocode isn't running
  38. let s:optionsEnabled = 0
  39. function! s:gocodeEnableOptions() abort
  40. if s:optionsEnabled
  41. return
  42. endif
  43. let bin_path = go#path#CheckBinPath("gocode")
  44. if empty(bin_path)
  45. return
  46. endif
  47. let s:optionsEnabled = 1
  48. call go#util#System(printf('%s set propose-builtins %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_propose_builtins', 1))))
  49. call go#util#System(printf('%s set autobuild %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_autobuild', 1))))
  50. call go#util#System(printf('%s set unimported-packages %s', go#util#Shellescape(bin_path), s:toBool(get(g:, 'go_gocode_unimported_packages', 0))))
  51. endfunction
  52. function! s:toBool(val) abort
  53. if a:val | return 'true ' | else | return 'false' | endif
  54. endfunction
  55. function! s:gocodeAutocomplete() abort
  56. call s:gocodeEnableOptions()
  57. " use the offset as is, because the cursor position is the position for
  58. " which autocomplete candidates are needed.
  59. return s:sync_gocode('autocomplete',
  60. \ [expand('%:p'), go#util#OffsetCursor()],
  61. \ go#util#GetLines())
  62. endfunction
  63. " go#complete#GoInfo returns the description of the identifier under the
  64. " cursor.
  65. function! go#complete#GetInfo() abort
  66. return s:sync_info(0)
  67. endfunction
  68. function! go#complete#Info(auto) abort
  69. if go#util#has_job()
  70. return s:async_info(a:auto)
  71. else
  72. return s:sync_info(a:auto)
  73. endif
  74. endfunction
  75. function! s:async_info(auto)
  76. if exists("s:async_info_job")
  77. call job_stop(s:async_info_job)
  78. unlet s:async_info_job
  79. endif
  80. let state = {
  81. \ 'exited': 0,
  82. \ 'exit_status': 0,
  83. \ 'closed': 0,
  84. \ 'messages': [],
  85. \ 'auto': a:auto
  86. \ }
  87. function! s:callback(chan, msg) dict
  88. let l:msg = a:msg
  89. if &encoding != 'utf-8'
  90. let l:msg = iconv(l:msg, 'utf-8', &encoding)
  91. endif
  92. call add(self.messages, l:msg)
  93. endfunction
  94. function! s:exit_cb(job, exitval) dict
  95. let self.exit_status = a:exitval
  96. let self.exited = 1
  97. if self.closed
  98. call self.complete()
  99. endif
  100. endfunction
  101. function! s:close_cb(ch) dict
  102. let self.closed = 1
  103. if self.exited
  104. call self.complete()
  105. endif
  106. endfunction
  107. function state.complete() dict
  108. if self.exit_status != 0
  109. return
  110. endif
  111. let result = s:info_filter(self.auto, join(self.messages, "\n"))
  112. call s:info_complete(self.auto, result)
  113. endfunction
  114. " add 1 to the offset, so that the position at the cursor will be included
  115. " in gocode's search
  116. let offset = go#util#OffsetCursor()+1
  117. " We might hit cache problems, as gocode doesn't handle different GOPATHs
  118. " well. See: https://github.com/nsf/gocode/issues/239
  119. let env = {
  120. \ "GOROOT": go#util#env("goroot")
  121. \ }
  122. let cmd = s:gocodeCommand('autocomplete',
  123. \ [expand('%:p'), offset])
  124. " TODO(bc): Don't write the buffer to a file; pass the buffer directrly to
  125. " gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
  126. " s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
  127. " should work.
  128. let options = {
  129. \ 'env': env,
  130. \ 'in_io': 'file',
  131. \ 'in_name': s:gocodeFile(),
  132. \ 'callback': funcref("s:callback", [], state),
  133. \ 'exit_cb': funcref("s:exit_cb", [], state),
  134. \ 'close_cb': funcref("s:close_cb", [], state)
  135. \ }
  136. let s:async_info_job = job_start(cmd, options)
  137. endfunction
  138. function! s:gocodeFile()
  139. let file = tempname()
  140. call writefile(go#util#GetLines(), file)
  141. return file
  142. endfunction
  143. function! s:sync_info(auto)
  144. " auto is true if we were called by g:go_auto_type_info's autocmd
  145. " add 1 to the offset, so that the position at the cursor will be included
  146. " in gocode's search
  147. let offset = go#util#OffsetCursor()+1
  148. let result = s:sync_gocode('autocomplete',
  149. \ [expand('%:p'), offset],
  150. \ go#util#GetLines())
  151. let result = s:info_filter(a:auto, result)
  152. call s:info_complete(a:auto, result)
  153. endfunction
  154. function! s:info_filter(auto, result) abort
  155. if empty(a:result)
  156. return ""
  157. endif
  158. let l:result = eval(a:result)
  159. if len(l:result) != 2
  160. return ""
  161. endif
  162. let l:candidates = l:result[1]
  163. if len(l:candidates) == 1
  164. " When gocode panics in vim mode, it returns
  165. " [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
  166. if a:auto && l:candidates[0].info ==# "PANIC PANIC PANIC"
  167. return ""
  168. endif
  169. return l:candidates[0].info
  170. endif
  171. let filtered = []
  172. let wordMatch = '\<' . expand("<cword>") . '\>'
  173. " escape single quotes in wordMatch before passing it to filter
  174. let wordMatch = substitute(wordMatch, "'", "''", "g")
  175. let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
  176. if len(l:filtered) != 1
  177. return ""
  178. endif
  179. return l:filtered[0].info
  180. endfunction
  181. function! s:info_complete(auto, result) abort
  182. if !empty(a:result)
  183. echo "vim-go: " | echohl Function | echon a:result | echohl None
  184. endif
  185. endfunction
  186. function! s:trim_bracket(val) abort
  187. let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '')
  188. return a:val
  189. endfunction
  190. let s:completions = ""
  191. function! go#complete#Complete(findstart, base) abort
  192. "findstart = 1 when we need to get the text length
  193. if a:findstart == 1
  194. execute "silent let s:completions = " . s:gocodeAutocomplete()
  195. return col('.') - s:completions[0] - 1
  196. "findstart = 0 when we need to return the list of completions
  197. else
  198. let s = getline(".")[col('.') - 1]
  199. if s =~ '[(){}\{\}]'
  200. return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
  201. endif
  202. return s:completions[1]
  203. endif
  204. endfunction
  205. function! go#complete#ToggleAutoTypeInfo() abort
  206. if get(g:, "go_auto_type_info", 0)
  207. let g:go_auto_type_info = 0
  208. call go#util#EchoProgress("auto type info disabled")
  209. return
  210. end
  211. let g:go_auto_type_info = 1
  212. call go#util#EchoProgress("auto type info enabled")
  213. endfunction
  214. " vim: sw=2 ts=2 et