cmd.vim 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. function! go#cmd#autowrite() abort
  2. if &autowrite == 1 || &autowriteall == 1
  3. silent! wall
  4. endif
  5. endfunction
  6. " Build builds the source code without producing any output binary. We live in
  7. " an editor so the best is to build it to catch errors and fix them. By
  8. " default it tries to call simply 'go build', but it first tries to get all
  9. " dependent files for the current folder and passes it to go build.
  10. function! go#cmd#Build(bang, ...) abort
  11. " Create our command arguments. go build discards any results when it
  12. " compiles multiple packages. So we pass the `errors` package just as an
  13. " placeholder with the current folder (indicated with '.'). We also pass -i
  14. " that tries to install the dependencies, this has the side effect that it
  15. " caches the build results, so every other build is faster.
  16. let args =
  17. \ ["build"] +
  18. \ map(copy(a:000), "expand(v:val)") +
  19. \ [".", "errors"]
  20. " Vim async.
  21. if go#util#has_job()
  22. if get(g:, 'go_echo_command_info', 1)
  23. call go#util#EchoProgress("building dispatched ...")
  24. endif
  25. call s:cmd_job({
  26. \ 'cmd': ['go'] + args,
  27. \ 'bang': a:bang,
  28. \ 'for': 'GoBuild',
  29. \})
  30. " Nvim async.
  31. elseif has('nvim')
  32. if get(g:, 'go_echo_command_info', 1)
  33. call go#util#EchoProgress("building dispatched ...")
  34. endif
  35. call go#jobcontrol#Spawn(a:bang, "build", "GoBuild", args)
  36. " Vim 7.4 without async
  37. else
  38. let default_makeprg = &makeprg
  39. let &makeprg = "go " . join(go#util#Shelllist(args), ' ')
  40. let l:listtype = go#list#Type("GoBuild")
  41. " execute make inside the source folder so we can parse the errors
  42. " correctly
  43. let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
  44. let dir = getcwd()
  45. try
  46. execute cd . fnameescape(expand("%:p:h"))
  47. if l:listtype == "locationlist"
  48. silent! exe 'lmake!'
  49. else
  50. silent! exe 'make!'
  51. endif
  52. redraw!
  53. finally
  54. execute cd . fnameescape(dir)
  55. endtry
  56. let errors = go#list#Get(l:listtype)
  57. call go#list#Window(l:listtype, len(errors))
  58. if !empty(errors) && !a:bang
  59. call go#list#JumpToFirst(l:listtype)
  60. else
  61. call go#util#EchoSuccess("[build] SUCCESS")
  62. endif
  63. let &makeprg = default_makeprg
  64. endif
  65. endfunction
  66. " BuildTags sets or shows the current build tags used for tools
  67. function! go#cmd#BuildTags(bang, ...) abort
  68. if a:0
  69. if a:0 == 1 && a:1 == '""'
  70. unlet g:go_build_tags
  71. call go#util#EchoSuccess("build tags are cleared")
  72. else
  73. let g:go_build_tags = a:1
  74. call go#util#EchoSuccess("build tags are changed to: ". a:1)
  75. endif
  76. return
  77. endif
  78. if !exists('g:go_build_tags')
  79. call go#util#EchoSuccess("build tags are not set")
  80. else
  81. call go#util#EchoSuccess("current build tags: ". g:go_build_tags)
  82. endif
  83. endfunction
  84. " Run runs the current file (and their dependencies if any) in a new terminal.
  85. function! go#cmd#RunTerm(bang, mode, files) abort
  86. if empty(a:files)
  87. let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
  88. else
  89. let cmd = "go run ". go#util#Shelljoin(map(copy(a:files), "expand(v:val)"), 1)
  90. endif
  91. call go#term#newmode(a:bang, cmd, a:mode)
  92. endfunction
  93. " Run runs the current file (and their dependencies if any) and outputs it.
  94. " This is intended to test small programs and play with them. It's not
  95. " suitable for long running apps, because vim is blocking by default and
  96. " calling long running apps will block the whole UI.
  97. function! go#cmd#Run(bang, ...) abort
  98. if has('nvim')
  99. call go#cmd#RunTerm(a:bang, '', a:000)
  100. return
  101. endif
  102. if go#util#has_job()
  103. " NOTE(arslan): 'term': 'open' case is not implement for +jobs. This means
  104. " executions waiting for stdin will not work. That's why we don't do
  105. " anything. Once this is implemented we're going to make :GoRun async
  106. endif
  107. if go#util#IsWin()
  108. exec '!go run ' . go#util#Shelljoin(go#tool#Files())
  109. if v:shell_error
  110. redraws! | echon "vim-go: [run] " | echohl ErrorMsg | echon "FAILED"| echohl None
  111. else
  112. redraws! | echon "vim-go: [run] " | echohl Function | echon "SUCCESS"| echohl None
  113. endif
  114. return
  115. endif
  116. " :make expands '%' and '#' wildcards, so they must also be escaped
  117. let default_makeprg = &makeprg
  118. if a:0 == 0
  119. let &makeprg = 'go run ' . go#util#Shelljoin(go#tool#Files(), 1)
  120. else
  121. let &makeprg = "go run " . go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
  122. endif
  123. let l:listtype = go#list#Type("GoRun")
  124. if l:listtype == "locationlist"
  125. exe 'lmake!'
  126. else
  127. exe 'make!'
  128. endif
  129. let items = go#list#Get(l:listtype)
  130. let errors = go#tool#FilterValids(items)
  131. call go#list#Populate(l:listtype, errors, &makeprg)
  132. call go#list#Window(l:listtype, len(errors))
  133. if !empty(errors) && !a:bang
  134. call go#list#JumpToFirst(l:listtype)
  135. endif
  136. let &makeprg = default_makeprg
  137. endfunction
  138. " Install installs the package by simple calling 'go install'. If any argument
  139. " is given(which are passed directly to 'go install') it tries to install
  140. " those packages. Errors are populated in the location window.
  141. function! go#cmd#Install(bang, ...) abort
  142. " use vim's job functionality to call it asynchronously
  143. if go#util#has_job()
  144. " expand all wildcards(i.e: '%' to the current file name)
  145. let goargs = map(copy(a:000), "expand(v:val)")
  146. if get(g:, 'go_echo_command_info', 1)
  147. call go#util#EchoProgress("installing dispatched ...")
  148. endif
  149. call s:cmd_job({
  150. \ 'cmd': ['go', 'install'] + goargs,
  151. \ 'bang': a:bang,
  152. \ 'for': 'GoInstall',
  153. \})
  154. return
  155. endif
  156. let default_makeprg = &makeprg
  157. " :make expands '%' and '#' wildcards, so they must also be escaped
  158. let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
  159. let &makeprg = "go install " . goargs
  160. let l:listtype = go#list#Type("GoInstall")
  161. " execute make inside the source folder so we can parse the errors
  162. " correctly
  163. let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
  164. let dir = getcwd()
  165. try
  166. execute cd . fnameescape(expand("%:p:h"))
  167. if l:listtype == "locationlist"
  168. silent! exe 'lmake!'
  169. else
  170. silent! exe 'make!'
  171. endif
  172. redraw!
  173. finally
  174. execute cd . fnameescape(dir)
  175. endtry
  176. let errors = go#list#Get(l:listtype)
  177. call go#list#Window(l:listtype, len(errors))
  178. if !empty(errors) && !a:bang
  179. call go#list#JumpToFirst(l:listtype)
  180. else
  181. call go#util#EchoSuccess("installed to ". go#path#Default())
  182. endif
  183. let &makeprg = default_makeprg
  184. endfunction
  185. " Generate runs 'go generate' in similar fashion to go#cmd#Build()
  186. function! go#cmd#Generate(bang, ...) abort
  187. let default_makeprg = &makeprg
  188. " :make expands '%' and '#' wildcards, so they must also be escaped
  189. let goargs = go#util#Shelljoin(map(copy(a:000), "expand(v:val)"), 1)
  190. if go#util#ShellError() != 0
  191. let &makeprg = "go generate " . goargs
  192. else
  193. let gofiles = go#util#Shelljoin(go#tool#Files(), 1)
  194. let &makeprg = "go generate " . goargs . ' ' . gofiles
  195. endif
  196. let l:listtype = go#list#Type("GoGenerate")
  197. echon "vim-go: " | echohl Identifier | echon "generating ..."| echohl None
  198. if l:listtype == "locationlist"
  199. silent! exe 'lmake!'
  200. else
  201. silent! exe 'make!'
  202. endif
  203. redraw!
  204. let errors = go#list#Get(l:listtype)
  205. call go#list#Window(l:listtype, len(errors))
  206. if !empty(errors)
  207. if !a:bang
  208. call go#list#JumpToFirst(l:listtype)
  209. endif
  210. else
  211. redraws! | echon "vim-go: " | echohl Function | echon "[generate] SUCCESS"| echohl None
  212. endif
  213. let &makeprg = default_makeprg
  214. endfunction
  215. " ---------------------
  216. " | Vim job callbacks |
  217. " ---------------------
  218. function s:cmd_job(args) abort
  219. let status_dir = expand('%:p:h')
  220. let started_at = reltime()
  221. call go#statusline#Update(status_dir, {
  222. \ 'desc': "current status",
  223. \ 'type': a:args.cmd[1],
  224. \ 'state': "started",
  225. \})
  226. " autowrite is not enabled for jobs
  227. call go#cmd#autowrite()
  228. function! s:complete(job, exit_status, data) closure abort
  229. let status = {
  230. \ 'desc': 'last status',
  231. \ 'type': a:args.cmd[1],
  232. \ 'state': "success",
  233. \ }
  234. if a:exit_status
  235. let status.state = "failed"
  236. endif
  237. let elapsed_time = reltimestr(reltime(started_at))
  238. " strip whitespace
  239. let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
  240. let status.state .= printf(" (%ss)", elapsed_time)
  241. call go#statusline#Update(status_dir, status)
  242. endfunction
  243. let a:args.complete = funcref('s:complete')
  244. let callbacks = go#job#Spawn(a:args)
  245. let start_options = {
  246. \ 'callback': callbacks.callback,
  247. \ 'exit_cb': callbacks.exit_cb,
  248. \ 'close_cb': callbacks.close_cb,
  249. \ }
  250. " pre start
  251. let dir = getcwd()
  252. let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
  253. let jobdir = fnameescape(expand("%:p:h"))
  254. execute cd . jobdir
  255. call job_start(a:args.cmd, start_options)
  256. " post start
  257. execute cd . fnameescape(dir)
  258. endfunction
  259. " vim: sw=2 ts=2 et