Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix async race #1656

Merged
merged 7 commits into from
Jan 28, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions autoload/go/cmd.vim
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ function s:cmd_job(args) abort
" autowrite is not enabled for jobs
call go#cmd#autowrite()

function! s:error_info_cb(job, exit_status, data) closure abort
function! s:complete(job, exit_status, data) closure abort
let status = {
\ 'desc': 'last status',
\ 'type': a:args.cmd[1],
Expand All @@ -288,12 +288,13 @@ function s:cmd_job(args) abort
call go#statusline#Update(status_dir, status)
endfunction

let a:args.error_info_cb = funcref('s:error_info_cb')
let a:args.complete = funcref('s:complete')
let callbacks = go#job#Spawn(a:args)

let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ }

" pre start
Expand Down
11 changes: 7 additions & 4 deletions autoload/go/coverage.vim
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function! go#coverage#Buffer(bang, ...) abort
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000,
\ 'custom_cb': function('s:coverage_callback', [l:tmpname]),
\ 'complete': function('s:coverage_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ })
Expand Down Expand Up @@ -107,7 +107,7 @@ function! go#coverage#Browser(bang, ...) abort
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname],
\ 'custom_cb': function('s:coverage_browser_callback', [l:tmpname]),
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ })
Expand Down Expand Up @@ -278,7 +278,8 @@ function s:coverage_job(args)
call go#cmd#autowrite()

let status_dir = expand('%:p:h')
function! s:error_info_cb(job, exit_status, data) closure
let Complete = a:args.complete
function! s:complete(job, exit_status, data) closure
let status = {
\ 'desc': 'last status',
\ 'type': "coverage",
Expand All @@ -290,14 +291,16 @@ function s:coverage_job(args)
endif

call go#statusline#Update(status_dir, status)
return Complete(a:job, a:exit_status, a:data)
endfunction

let a:args.error_info_cb = funcref('s:error_info_cb')
let a:args.complete = funcref('s:complete')
let callbacks = go#job#Spawn(a:args)

let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ }

" pre start
Expand Down
8 changes: 2 additions & 6 deletions autoload/go/def.vim
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ function! go#def#Jump(mode) abort
if go#util#has_job()
let l:spawn_args = {
\ 'cmd': cmd,
\ 'custom_cb': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
\ }

if &modified
Expand Down Expand Up @@ -292,16 +292,12 @@ function! go#def#Stack(...) abort
endfunction

function s:def_job(args) abort
function! s:error_info_cb(job, exit_status, data) closure
" do not print anything during async definition search&jump
endfunction

let a:args.error_info_cb = funcref('s:error_info_cb')
let callbacks = go#job#Spawn(a:args)

let start_options = {
\ 'callback': callbacks.callback,
\ 'exit_cb': callbacks.exit_cb,
\ 'close_cb': callbacks.close_cb,
\ }

if &modified
Expand Down
16 changes: 16 additions & 0 deletions autoload/go/guru.vim
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,12 @@ function! s:async_guru(args) abort

let status = {}
let exitval = 0
let closed = 0
let exited = 0

function! s:exit_cb(job, exitval) closure
let exited = 1

let status = {
\ 'desc': 'last status',
\ 'type': statusline_type,
Expand All @@ -176,9 +180,21 @@ function! s:async_guru(args) abort
endif

call go#statusline#Update(status_dir, status)

if closed
call s:complete()
endif
endfunction

function! s:close_cb(ch) closure
let closed = 1

if exited
call s:complete()
endif
endfunction

function! s:complete() closure
let out = join(messages, "\n")

if has_key(a:args, 'custom_parse')
Expand Down
151 changes: 96 additions & 55 deletions autoload/go/job.vim
Original file line number Diff line number Diff line change
@@ -1,44 +1,78 @@
" Spawn returns callbacks to be used with job_start. It's abstracted to be
" used with various go command, such as build, test, install, etc.. This avoid
" us to write the same callback over and over for some commands. It's fully
" customizable so each command can change it to it's own logic.
" Spawn returns callbacks to be used with job_start. It is abstracted to be
" used with various go commands, such as build, test, install, etc.. This
" allows us to avoid writing the same callback over and over for some
" commands. It's fully customizable so each command can change it to it's own
" logic.
"
" args is a dictionary with the these keys:
" 'cmd':
" The value to pass to job_start().
" 'bang':
" Set to 0 to jump to the first error in the error list.
" Defaults to 0.
" 'for':
" The g:go_list_type_command key to use to get the error list type to use.
" Defaults to '_job'
" 'complete':
" A function to call after the job exits and the channel is closed. The
" function will be passed three arguments: the job, its exit code, and the
" list of messages received from the channel. The default value will
" process the messages and manage the error list after the job exits and
" the channel is closed.
" 'callback':
" A function to call when there is a message to read from the job's
" channel. The function will be passed two arguments: the channel and a
" message. See job-callback.

" The return value is a dictionary with these keys:
" 'callback':
" A function suitable to be passed as a job callback handler. See
" job-callback.
" 'exit_cb':
" A function suitable to be passed as a job exit_cb handler. See
" job-exit_cb.
" 'close_cb':
" A function suitable to be passed as a job close_cb handler. See
" job-close_cb.
function go#job#Spawn(args)
let cbs = {
\ 'winnr': winnr(),
\ 'dir': getcwd(),
\ 'jobdir': fnameescape(expand("%:p:h")),
\ 'messages': [],
\ 'args': a:args.cmd,
\ 'bang': 0,
\ 'for': "_job",
\ }
let cbs = {}

let winnr = winnr()
let dir = getcwd()
let jobdir = fnameescape(expand("%:p:h"))
let messages = []
let args = a:args.cmd
let bang = 0
let for = "_job"

if has_key(a:args, 'bang')
let cbs.bang = a:args.bang
let l:bang = a:args.bang
endif

if has_key(a:args, 'for')
let cbs.for = a:args.for
let l:for = a:args.for
endif

" add final callback to be called if async job is finished
" The signature should be in form: func(job, exit_status, messages)
if has_key(a:args, 'custom_cb')
let cbs.custom_cb = a:args.custom_cb
endif
let l:exited = 0
let l:exit_status = 0
let l:closed = 0

function! s:NopComplete(job, exit_status, data)
endfunction

let Complete = funcref('s:NopComplete')

if has_key(a:args, 'error_info_cb')
let cbs.error_info_cb = a:args.error_info_cb
if has_key(a:args, 'complete')
let Complete = a:args.complete
endif

function cbs.callback(chan, msg) dict
call add(self.messages, a:msg)
function cbs.callback(chan, msg) dict closure
call add(messages, a:msg)
endfunction

function cbs.exit_cb(job, exitval) dict
if has_key(self, 'error_info_cb')
call self.error_info_cb(a:job, a:exitval, self.messages)
endif
function cbs.exit_cb(job, exitval) dict closure
let exit_status = a:exitval
let exited = 1

if get(g:, 'go_echo_command_info', 1)
if a:exitval == 0
Expand All @@ -48,55 +82,62 @@ function go#job#Spawn(args)
endif
endif

if has_key(self, 'custom_cb')
call self.custom_cb(a:job, a:exitval, self.messages)
if closed
call Complete(a:job, exit_status, messages)
call s:show_errors(a:job, exit_status, messages)
endif
endfunction

function cbs.close_cb(ch) dict closure
let closed = 1

if exited
let job = ch_getjob(a:ch)
call Complete(job, exit_status, messages)
call s:show_errors(job, exit_status, messages)
endif
endfunction

let l:listtype = go#list#Type(self.for)
if a:exitval == 0
function! s:show_errors(job, exit_status, data) closure
let l:listtype = go#list#Type(for)
if a:exit_status == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif

call self.show_errors(l:listtype)
endfunction
let l:listtype = go#list#Type(for)
if len(a:data) == 0
call go#list#Clean(l:listtype)
call go#list#Window(l:listtype)
return
endif

function cbs.show_errors(listtype) dict
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
try
execute cd self.jobdir
let errors = go#tool#ParseErrors(self.messages)
execute cd jobdir
let errors = go#tool#ParseErrors(a:data)
let errors = go#tool#FilterValids(errors)
finally
execute cd . fnameescape(self.dir)
execute cd . fnameescape(dir)
endtry

if !len(errors)
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(self.messages + [self.dir])
call go#util#EchoError(messages + [dir])
return
endif

if self.winnr == winnr()
call go#list#Populate(a:listtype, errors, join(self.args))
call go#list#Window(a:listtype, len(errors))
if !empty(errors) && !self.bang
call go#list#JumpToFirst(a:listtype)
if winnr == winnr()
call go#list#Populate(l:listtype, errors, join(args))
call go#list#Window(l:listtype, len(errors))
if !bang
call go#list#JumpToFirst(l:listtype)
endif
endif
endfunction

" override callback handler if user provided it
if has_key(a:args, 'callback')
let cbs.callback = a:args.callback
endif

" override exit callback handler if user provided it
if has_key(a:args, 'exit_cb')
let cbs.exit_cb = a:args.exit_cb
endif

return cbs
endfunction

" vim: sw=2 ts=2 et
Loading