123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357 |
- if !exists("g:go_metalinter_command")
- let g:go_metalinter_command = ""
- endif
- if !exists("g:go_metalinter_autosave_enabled")
- let g:go_metalinter_autosave_enabled = ['vet', 'golint']
- endif
- if !exists("g:go_metalinter_enabled")
- let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
- endif
- if !exists("g:go_metalinter_disabled")
- let g:go_metalinter_disabled = []
- endif
- if !exists("g:go_golint_bin")
- let g:go_golint_bin = "golint"
- endif
- if !exists("g:go_errcheck_bin")
- let g:go_errcheck_bin = "errcheck"
- endif
- function! go#lint#Gometa(autosave, ...) abort
- if a:0 == 0
- let goargs = [expand('%:p:h')]
- else
- let goargs = a:000
- endif
- let bin_path = go#path#CheckBinPath("gometalinter")
- if empty(bin_path)
- return
- endif
- let cmd = [bin_path]
- let cmd += ["--disable-all"]
- if a:autosave || empty(g:go_metalinter_command)
- " linters
- let linters = a:autosave ? g:go_metalinter_autosave_enabled : g:go_metalinter_enabled
- for linter in linters
- let cmd += ["--enable=".linter]
- endfor
- for linter in g:go_metalinter_disabled
- let cmd += ["--disable=".linter]
- endfor
- " gometalinter has a --tests flag to tell its linters whether to run
- " against tests. While not all of its linters respect this flag, for those
- " that do, it means if we don't pass --tests, the linter won't run against
- " test files. One example of a linter that will not run against tests if
- " we do not specify this flag is errcheck.
- let cmd += ["--tests"]
- else
- " the user wants something else, let us use it.
- let cmd += split(g:go_metalinter_command, " ")
- endif
- if a:autosave
- " redraw so that any messages that were displayed while writing the file
- " will be cleared
- redraw
- " Include only messages for the active buffer for autosave.
- let cmd += [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
- endif
- " gometalinter has a default deadline of 5 seconds.
- "
- " For async mode (s:lint_job), we want to override the default deadline only
- " if we have a deadline configured.
- "
- " For sync mode (go#util#System), always explicitly pass the 5 seconds
- " deadline if there is no other deadline configured. If a deadline is
- " configured, then use it.
- " Call gometalinter asynchronously.
- if go#util#has_job() && has('lambda')
- let deadline = get(g:, 'go_metalinter_deadline', 0)
- if deadline != 0
- let cmd += ["--deadline=" . deadline]
- endif
- let cmd += goargs
- call s:lint_job({'cmd': cmd}, a:autosave)
- return
- endif
- " We're calling gometalinter synchronously.
- let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")]
- let cmd += goargs
- let [l:out, l:err] = go#util#Exec(cmd)
- if a:autosave
- let l:listtype = go#list#Type("GoMetaLinterAutoSave")
- else
- let l:listtype = go#list#Type("GoMetaLinter")
- endif
- if l:err == 0
- call go#list#Clean(l:listtype)
- echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
- else
- " GoMetaLinter can output one of the two, so we look for both:
- " <file>:<line>:[<column>]: <message> (<linter>)
- " <file>:<line>:: <message> (<linter>)
- " This can be defined by the following errorformat:
- let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m"
- " Parse and populate our location list
- call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
- let errors = go#list#Get(l:listtype)
- call go#list#Window(l:listtype, len(errors))
- if !a:autosave
- call go#list#JumpToFirst(l:listtype)
- endif
- endif
- endfunction
- " Golint calls 'golint' on the current directory. Any warnings are populated in
- " the location list
- function! go#lint#Golint(...) abort
- let bin_path = go#path#CheckBinPath(g:go_golint_bin)
- if empty(bin_path)
- return
- endif
- let bin_path = go#util#Shellescape(bin_path)
- if a:0 == 0
- let out = go#util#System(bin_path . " " . go#util#Shellescape(go#package#ImportPath()))
- else
- let out = go#util#System(bin_path . " " . go#util#Shelljoin(a:000))
- endif
- if empty(out)
- echon "vim-go: " | echohl Function | echon "[lint] PASS" | echohl None
- return
- endif
- let l:listtype = go#list#Type("GoLint")
- call go#list#Parse(l:listtype, out, "GoLint")
- let errors = go#list#Get(l:listtype)
- call go#list#Window(l:listtype, len(errors))
- call go#list#JumpToFirst(l:listtype)
- endfunction
- " Vet calls 'go vet' on the current directory. Any warnings are populated in
- " the location list
- function! go#lint#Vet(bang, ...) abort
- call go#cmd#autowrite()
- echon "vim-go: " | echohl Identifier | echon "calling vet..." | echohl None
- if a:0 == 0
- let out = go#util#System('go vet ' . go#util#Shellescape(go#package#ImportPath()))
- else
- let out = go#util#System('go tool vet ' . go#util#Shelljoin(a:000))
- endif
- let l:listtype = go#list#Type("GoVet")
- if go#util#ShellError() != 0
- let errorformat="%-Gexit status %\\d%\\+," . &errorformat
- call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
- let errors = go#list#Get(l:listtype)
- call go#list#Window(l:listtype, len(errors))
- if !empty(errors) && !a:bang
- call go#list#JumpToFirst(l:listtype)
- endif
- echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None
- else
- call go#list#Clean(l:listtype)
- redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None
- endif
- endfunction
- " ErrCheck calls 'errcheck' for the given packages. Any warnings are populated in
- " the location list
- function! go#lint#Errcheck(...) abort
- if a:0 == 0
- let import_path = go#package#ImportPath()
- if import_path == -1
- echohl Error | echomsg "vim-go: package is not inside GOPATH src" | echohl None
- return
- endif
- else
- let import_path = go#util#Shelljoin(a:000)
- endif
- let bin_path = go#path#CheckBinPath(g:go_errcheck_bin)
- if empty(bin_path)
- return
- endif
- echon "vim-go: " | echohl Identifier | echon "errcheck analysing ..." | echohl None
- redraw
- let command = go#util#Shellescape(bin_path) . ' -abspath ' . import_path
- let out = go#tool#ExecuteInDir(command)
- let l:listtype = go#list#Type("GoErrCheck")
- if go#util#ShellError() != 0
- let errformat = "%f:%l:%c:\ %m, %f:%l:%c\ %#%m"
- " Parse and populate our location list
- call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'Errcheck')
- let errors = go#list#Get(l:listtype)
- if empty(errors)
- echohl Error | echomsg "GoErrCheck returned error" | echohl None
- echo out
- return
- endif
- if !empty(errors)
- echohl Error | echomsg "GoErrCheck found errors" | echohl None
- call go#list#Populate(l:listtype, errors, 'Errcheck')
- call go#list#Window(l:listtype, len(errors))
- if !empty(errors)
- call go#list#JumpToFirst(l:listtype)
- endif
- endif
- else
- call go#list#Clean(l:listtype)
- echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None
- endif
- endfunction
- function! go#lint#ToggleMetaLinterAutoSave() abort
- if get(g:, "go_metalinter_autosave", 0)
- let g:go_metalinter_autosave = 0
- call go#util#EchoProgress("auto metalinter disabled")
- return
- end
- let g:go_metalinter_autosave = 1
- call go#util#EchoProgress("auto metalinter enabled")
- 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,
- \ 'winnr': 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()
- if a:autosave
- let state.listtype = go#list#Type("GoMetaLinterAutoSave")
- else
- let state.listtype = go#list#Type("GoMetaLinter")
- 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:winnr = 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'
- exe self.winnr . "wincmd w"
- 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))
- " 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
- exe l:winnr . "wincmd w"
- endif
- if get(g:, 'go_echo_command_info', 1)
- 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),
- \ }
- call job_start(a:args.cmd, start_options)
- if get(g:, 'go_echo_command_info', 1)
- call go#util#EchoProgress("linting started ...")
- endif
- endfunction
- " vim: sw=2 ts=2 et
|