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

run gometalinter asynchronously in Neovim #1901

Merged
merged 3 commits into from
Aug 6, 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
36 changes: 24 additions & 12 deletions autoload/go/job.vim
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ endfunction
" 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.
Expand All @@ -32,9 +30,9 @@ endfunction
" '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.
" list of messages received from the channel. The default is a no-op. A
" custom value can modify the messages before they are processed by the
" returned exit_cb and close_cb callbacks.

" The return value is a dictionary with these keys:
" 'callback':
Expand All @@ -47,7 +45,9 @@ endfunction
" A function suitable to be passed as a job close_cb handler. See
" job-close_cb.
" 'cwd':
" The path to the directory which contains the current buffer.
" The path to the directory which contains the current buffer. The
" callbacks are configured to expect this directory is the working
" directory for the job; it should not be modified by callers.
function! go#job#Options(args)
let cbs = {}
let state = {
Expand Down Expand Up @@ -185,6 +185,10 @@ function! go#job#Options(args)

function state.show_errors(job, exit_status, data)
let l:winid = win_getid(winnr())
" Always set the active window to the window that was active when the job
" was started. Among other things, this makes sure that the correct
" window's location list will be populated when the list type is
" 'location' and the user has moved windows since starting the job.
call win_gotoid(self.winid)

let l:listtype = go#list#Type(self.for)
Expand Down Expand Up @@ -229,17 +233,20 @@ function! go#job#Options(args)
endif
endfunction

if has('nvim')
return s:neooptions(cbs)
endif

return cbs
endfunction

" go#job#Start runs a job. The options are expected to be the options
" suitable for Vim8 jobs. When called from Neovim, Vim8 options will be
" transformed to their Neovim equivalents.
function! go#job#Start(cmd, options)
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
let l:options = copy(a:options)

if has('nvim')
let l:options = s:neooptions(l:options)
endif

if !has_key(l:options, 'cwd')
" pre start
let dir = getcwd()
Expand All @@ -249,10 +256,11 @@ function! go#job#Start(cmd, options)
if has_key(l:options, '_start')
call l:options._start()
" remove _start to play nicely with vim (when vim encounters an unexpected
" job option it reports an "E475: invalid argument" error.
" job option it reports an "E475: invalid argument" error).
unlet l:options._start
endif


if has('nvim')
let l:input = []
if has_key(l:options, 'in_io') && l:options.in_io ==# 'file' && !empty(l:options.in_name)
Expand Down Expand Up @@ -286,6 +294,11 @@ function! s:neooptions(options)
let l:options['stderr_buf'] = ''

for key in keys(a:options)
if key == 'cwd'
let l:options['cwd'] = a:options['cwd']
continue
endif

if key == 'callback'
let l:options['callback'] = a:options['callback']

Expand Down Expand Up @@ -402,5 +415,4 @@ function! s:neooptions(options)
return l:options
endfunction


" vim: sw=2 ts=2 et
129 changes: 33 additions & 96 deletions autoload/go/lint.vim
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ function! go#lint#Gometa(autosave, ...) abort
redraw

" Include only messages for the active buffer for autosave.
let cmd += [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
let include = [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
if go#util#has_job() || has('nvim')
let include = [printf('--include=^%s:.*$', expand('%:p:t'))]
endif
let cmd += include
endif

" Call gometalinter asynchronously.
Expand All @@ -52,7 +56,7 @@ function! go#lint#Gometa(autosave, ...) abort

let cmd += goargs

if go#util#has_job() && has('lambda')
if go#util#has_job() || has('nvim')
call s:lint_job({'cmd': cmd}, a:autosave)
return
endif
Expand Down Expand Up @@ -193,113 +197,46 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
endfunction

function! s:lint_job(args, autosave)
let state = {
\ 'status_dir': expand('%:p:h'),
\ 'started_at': reltime(),
\ 'messages': [],
\ 'exited': 0,
\ 'closed': 0,
\ 'exit_status': 0,
\ 'winid': win_getid(winnr()),
\ 'autosave': a:autosave
\ }

call go#statusline#Update(state.status_dir, {
\ 'desc': "current status",
\ 'type': "gometalinter",
\ 'state': "analysing",
\})

" autowrite is not enabled for jobs
call go#cmd#autowrite()
let l:opts = {
\ 'statustype': "gometalinter",
\ 'errorformat': '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m',
\ 'for': "GoMetaLinter",
\ }

if a:autosave
let state.listtype = go#list#Type("GoMetaLinterAutoSave")
else
let state.listtype = go#list#Type("GoMetaLinter")
let l:opts.for = "GoMetaLinterAutoSave"
endif

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

function! s:exit_cb(job, exitval) dict
let self.exited = 1
let self.exit_status = a:exitval

let status = {
\ 'desc': 'last status',
\ 'type': "gometaliner",
\ 'state': "finished",
\ }

if a:exitval
let status.state = "failed"
endif

let elapsed_time = reltimestr(reltime(self.started_at))
" strip whitespace
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
let status.state .= printf(" (%ss)", elapsed_time)

call go#statusline#Update(self.status_dir, status)

if self.closed
call self.show_errors()
endif
endfunction

function! s:close_cb(ch) dict
let self.closed = 1

if self.exited
call self.show_errors()
endif
endfunction

function state.show_errors()
let l:winid = win_getid(winnr())

" make sure the current window is the window from which gometalinter was
" run when the listtype is locationlist so that the location list for the
" correct window will be populated.
if self.listtype == 'locationlist'
call win_gotoid(self.winid)
endif

let l:errorformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
call go#list#ParseFormat(self.listtype, l:errorformat, self.messages, 'GoMetaLinter')

let errors = go#list#Get(self.listtype)
call go#list#Window(self.listtype, len(errors))
let l:cbs = go#job#Options(l:opts)

if a:autosave
" move to the window that was active before processing the errors, because
" the user may have moved around within the window or even moved to a
" different window since saving. Moving back to current window as of the
" start of this function avoids the perception that the quickfix window
" steals focus when linting takes a while.
if self.autosave
call win_gotoid(self.winid)
endif

if go#config#EchoCommandInfo()
call go#util#EchoSuccess("linting finished")
endif
endfunction

" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': funcref("s:callback", [], state),
\ 'exit_cb': funcref("s:exit_cb", [], state),
\ 'close_cb': funcref("s:close_cb", [], state),
\ }
function! s:exit_cb(next, job, exitval)
let l:winid = win_getid(winnr())
call call(a:next, [a:job, a:exitval])
call win_gotoid(l:winid)
endfunction
" wrap l:cbs.exit_cb in s:exit_cb.
let l:cbs.exit_cb = funcref('s:exit_cb', [l:cbs.exit_cb])

call job_start(a:args.cmd, start_options)

if go#config#EchoCommandInfo()
call go#util#EchoProgress("linting started ...")
function! s:close_cb(next, ch)
let l:winid = win_getid(winnr())
call call(a:next, [a:ch])
call win_gotoid(l:winid)
endfunction
" wrap l:cbs.close_cb in s:close_cb.
let l:cbs.close_cb = funcref('s:close_cb', [l:cbs.close_cb])
endif

" autowrite is not enabled for jobs
call go#cmd#autowrite()

call go#job#Start(a:args.cmd, l:cbs)
endfunction

" vim: sw=2 ts=2 et
21 changes: 0 additions & 21 deletions autoload/go/lint_test.vim
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ func! Test_Gometa() abort
" clear the quickfix lists
call setqflist([], 'r')

" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
" be autoloaded and the default for g:go_metalinter_enabled will be set so
" we can capture it to restore it after the test is run.
silent call go#lint#ToggleMetaLinterAutoSave()
" And restore it back to its previous value
silent call go#lint#ToggleMetaLinterAutoSave()

let g:go_metalinter_enabled = ['golint']

call go#lint#Gometa(0, $GOPATH . '/src/foo')
Expand All @@ -42,13 +35,6 @@ func! Test_GometaWithDisabled() abort
" clear the quickfix lists
call setqflist([], 'r')

" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
" be autoloaded and the default for g:go_metalinter_disabled will be set so
" we can capture it to restore it after the test is run.
silent call go#lint#ToggleMetaLinterAutoSave()
" And restore it back to its previous value
silent call go#lint#ToggleMetaLinterAutoSave()

let g:go_metalinter_disabled = ['vet']

call go#lint#Gometa(0, $GOPATH . '/src/foo')
Expand Down Expand Up @@ -77,13 +63,6 @@ func! Test_GometaAutoSave() abort
" clear the location lists
call setloclist(l:winnr, [], 'r')

" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
" be autoloaded and the default for g:go_metalinter_autosave_enabled will be
" set so we can capture it to restore it after the test is run.
silent call go#lint#ToggleMetaLinterAutoSave()
" And restore it back to its previous value
silent call go#lint#ToggleMetaLinterAutoSave()

let g:go_metalinter_autosave_enabled = ['golint']

call go#lint#Gometa(1)
Expand Down
2 changes: 1 addition & 1 deletion autoload/go/util.vim
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ endfunction

" Check if Vim jobs API is supported.
"
" The (optional) first paramter can be added to indicate the 'cwd' or 'env'
" The (optional) first parameter can be added to indicate the 'cwd' or 'env'
" parameters will be used, which wasn't added until a later version.
function! go#util#has_job(...) abort
" cwd and env parameters to job_start was added in this version.
Expand Down