job.vim 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. " Spawn returns callbacks to be used with job_start. It is abstracted to be
  2. " used with various go commands, such as build, test, install, etc.. This
  3. " allows us to avoid writing the same callback over and over for some
  4. " commands. It's fully customizable so each command can change it to it's own
  5. " logic.
  6. "
  7. " args is a dictionary with the these keys:
  8. " 'cmd':
  9. " The value to pass to job_start().
  10. " 'bang':
  11. " Set to 0 to jump to the first error in the error list.
  12. " Defaults to 0.
  13. " 'for':
  14. " The g:go_list_type_command key to use to get the error list type to use.
  15. " Defaults to '_job'
  16. " 'complete':
  17. " A function to call after the job exits and the channel is closed. The
  18. " function will be passed three arguments: the job, its exit code, and the
  19. " list of messages received from the channel. The default value will
  20. " process the messages and manage the error list after the job exits and
  21. " the channel is closed.
  22. " The return value is a dictionary with these keys:
  23. " 'callback':
  24. " A function suitable to be passed as a job callback handler. See
  25. " job-callback.
  26. " 'exit_cb':
  27. " A function suitable to be passed as a job exit_cb handler. See
  28. " job-exit_cb.
  29. " 'close_cb':
  30. " A function suitable to be passed as a job close_cb handler. See
  31. " job-close_cb.
  32. function go#job#Spawn(args)
  33. let cbs = {}
  34. let state = {
  35. \ 'winnr': winnr(),
  36. \ 'dir': getcwd(),
  37. \ 'jobdir': fnameescape(expand("%:p:h")),
  38. \ 'messages': [],
  39. \ 'args': a:args.cmd,
  40. \ 'bang': 0,
  41. \ 'for': "_job",
  42. \ 'exited': 0,
  43. \ 'exit_status': 0,
  44. \ 'closed': 0,
  45. \ 'errorformat': &errorformat
  46. \ }
  47. if has_key(a:args, 'bang')
  48. let state.bang = a:args.bang
  49. endif
  50. if has_key(a:args, 'for')
  51. let state.for = a:args.for
  52. endif
  53. " do nothing in state.complete by default.
  54. function state.complete(job, exit_status, data)
  55. endfunction
  56. if has_key(a:args, 'complete')
  57. let state.complete = a:args.complete
  58. endif
  59. function! s:callback(chan, msg) dict
  60. call add(self.messages, a:msg)
  61. endfunction
  62. " explicitly bind callback to state so that within it, self will
  63. " always refer to state. See :help Partial for more information.
  64. let cbs.callback = function('s:callback', [], state)
  65. function! s:exit_cb(job, exitval) dict
  66. let self.exit_status = a:exitval
  67. let self.exited = 1
  68. if get(g:, 'go_echo_command_info', 1)
  69. if a:exitval == 0
  70. call go#util#EchoSuccess("SUCCESS")
  71. else
  72. call go#util#EchoError("FAILED")
  73. endif
  74. endif
  75. if self.closed
  76. call self.complete(a:job, self.exit_status, self.messages)
  77. call self.show_errors(a:job, self.exit_status, self.messages)
  78. endif
  79. endfunction
  80. " explicitly bind exit_cb to state so that within it, self will always refer
  81. " to state. See :help Partial for more information.
  82. let cbs.exit_cb = function('s:exit_cb', [], state)
  83. function! s:close_cb(ch) dict
  84. let self.closed = 1
  85. if self.exited
  86. let job = ch_getjob(a:ch)
  87. call self.complete(job, self.exit_status, self.messages)
  88. call self.show_errors(job, self.exit_status, self.messages)
  89. endif
  90. endfunction
  91. " explicitly bind close_cb to state so that within it, self will
  92. " always refer to state. See :help Partial for more information.
  93. let cbs.close_cb = function('s:close_cb', [], state)
  94. function state.show_errors(job, exit_status, data)
  95. let l:listtype = go#list#Type(self.for)
  96. if a:exit_status == 0
  97. call go#list#Clean(l:listtype)
  98. return
  99. endif
  100. let l:listtype = go#list#Type(self.for)
  101. if len(a:data) == 0
  102. call go#list#Clean(l:listtype)
  103. return
  104. endif
  105. let out = join(self.messages, "\n")
  106. let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
  107. try
  108. " parse the errors relative to self.jobdir
  109. execute cd self.jobdir
  110. call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
  111. let errors = go#list#Get(l:listtype)
  112. finally
  113. execute cd . fnameescape(self.dir)
  114. endtry
  115. if empty(errors)
  116. " failed to parse errors, output the original content
  117. call go#util#EchoError(self.messages + [self.dir])
  118. return
  119. endif
  120. if self.winnr == winnr()
  121. call go#list#Window(l:listtype, len(errors))
  122. if !self.bang
  123. call go#list#JumpToFirst(l:listtype)
  124. endif
  125. endif
  126. endfunction
  127. return cbs
  128. endfunction
  129. " vim: sw=2 ts=2 et