" s:jobs is a global reference to all jobs started with Spawn() or with the " internal function s:spawn let s:jobs = {} " s:handlers is a global event handlers for all jobs started with Spawn() or " with the internal function s:spawn let s:handlers = {} " Spawn is a wrapper around s:spawn. It can be executed by other files and " scripts if needed. Desc defines the description for printing the status " during the job execution (useful for statusline integration). function! go#jobcontrol#Spawn(bang, desc, args) " autowrite is not enabled for jobs call go#cmd#autowrite() let job = s:spawn(a:bang, a:desc, a:args) return job.id endfunction " Statusline returns the current status of the job function! go#jobcontrol#Statusline() abort if empty(s:jobs) return '' endif let import_path = go#package#ImportPath(expand('%:p:h')) for job in values(s:jobs) if job.importpath != import_path continue endif if job.state == "SUCCESS" return '' endif return printf("%s ... [%s]", job.desc, job.state) endfor return '' endfunction " AddHandler adds a on_exit callback handler and returns the id. function! go#jobcontrol#AddHandler(handler) let i = len(s:handlers) while has_key(s:handlers, string(i)) let i += 1 break endwhile let s:handlers[string(i)] = a:handler return string(i) endfunction " RemoveHandler removes a callback handler by id. function! go#jobcontrol#RemoveHandler(id) unlet s:handlers[a:id] endfunction " spawn spawns a go subcommand with the name and arguments with jobstart. Once " a job is started a reference will be stored inside s:jobs. spawn changes the " GOPATH when g:go_autodetect_gopath is enabled. The job is started inside the " current files folder. function! s:spawn(bang, desc, args) let job = { \ 'desc': a:desc, \ 'bang': a:bang, \ 'winnr': winnr(), \ 'importpath': go#package#ImportPath(expand('%:p:h')), \ 'state': "RUNNING", \ 'stderr' : [], \ 'stdout' : [], \ 'on_stdout': function('s:on_stdout'), \ 'on_stderr': function('s:on_stderr'), \ 'on_exit' : function('s:on_exit'), \ } " modify GOPATH if needed let old_gopath = $GOPATH let $GOPATH = go#path#Detect() " execute go build in the files directory let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' " cleanup previous jobs for this file for jb in values(s:jobs) if jb.importpath == job.importpath unlet s:jobs[jb.id] endif endfor let dir = getcwd() let jobdir = fnameescape(expand("%:p:h")) execute cd . jobdir " append the subcommand, such as 'build' let argv = ['go'] + a:args " run, forrest, run! let id = jobstart(argv, job) let job.id = id let job.dir = jobdir let s:jobs[id] = job execute cd . fnameescape(dir) " restore back GOPATH let $GOPATH = old_gopath return job endfunction " on_exit is the exit handler for jobstart(). It handles cleaning up the job " references and also displaying errors in the quickfix window collected by " on_stderr handler. If there are no errors and a quickfix window is open, " it'll be closed. function! s:on_exit(job_id, exit_status) let std_combined = self.stderr + self.stdout call s:callback_handlers_on_exit(s:jobs[a:job_id], a:exit_status, std_combined) if a:exit_status == 0 call go#list#Clean(0) call go#list#Window(0) let self.state = "SUCCESS" call go#util#EchoSuccess("SUCCESS") return endif let self.state = "FAILED" call go#util#EchoError("FAILED") let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' let dir = getcwd() try execute cd self.dir let errors = go#tool#ParseErrors(std_combined) let errors = go#tool#FilterValids(errors) finally execute cd . fnameescape(dir) endtry if !len(errors) " failed to parse errors, output the original content call go#util#EchoError(std_combined[0]) return endif " if we are still in the same windows show the list if self.winnr == winnr() let l:listtype = "locationlist" call go#list#Populate(l:listtype, errors) call go#list#Window(l:listtype, len(errors)) if !empty(errors) && !self.bang call go#list#JumpToFirst(l:listtype) endif endif endfunction " callback_handlers_on_exit runs all handlers for job on exit event. function! s:callback_handlers_on_exit(job, exit_status, data) if empty(s:handlers) return endif for s:handler in values(s:handlers) call s:handler(a:job, a:exit_status, a:data) endfor endfunction " on_stdout is the stdout handler for jobstart(). It collects the output of " stderr and stores them to the jobs internal stdout list. function! s:on_stdout(job_id, data) call extend(self.stdout, a:data) endfunction " on_stderr is the stderr handler for jobstart(). It collects the output of " stderr and stores them to the jobs internal stderr list. function! s:on_stderr(job_id, data) call extend(self.stderr, a:data) endfunction " abort_all aborts all current jobs created with s:spawn() function! s:abort_all() if empty(s:jobs) return endif for id in keys(s:jobs) if id > 0 silent! call jobstop(id) endif endfor let s:jobs = {} endfunction " abort aborts the job with the given name, where name is the first argument " passed to s:spawn() function! s:abort(path) if empty(s:jobs) return endif for job in values(s:jobs) if job.importpath == path && job.id > 0 silent! call jobstop(job.id) unlet s:jobs['job.id'] endif endfor endfunction " vim:ts=2:sw=2:et