Skip to content

Commit

Permalink
Merge pull request #2182 from micahcoffman/golangci_linter_support
Browse files Browse the repository at this point in the history
add support for golangci-lint
  • Loading branch information
bhcleek authored Mar 24, 2019
2 parents 5eab407 + a62c7ce commit 2774621
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 82 deletions.
18 changes: 15 additions & 3 deletions autoload/go/config.vim
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,27 @@ function! go#config#SetTemplateAutocreate(value) abort
endfunction

function! go#config#MetalinterCommand() abort
return get(g:, "go_metalinter_command", "")
return get(g:, "go_metalinter_command", "gometalinter")
endfunction

function! go#config#MetalinterAutosaveEnabled() abort
return get(g:, 'go_metalinter_autosave_enabled', ['vet', 'golint'])
let l:default_enabled = ["vet", "golint"]

if go#config#MetalinterCommand() == "golangci-lint"
let l:default_enabled = ["govet", "golint"]
endif

return get(g:, "go_metalinter_autosave_enabled", default_enabled)
endfunction

function! go#config#MetalinterEnabled() abort
return get(g:, "go_metalinter_enabled", ['vet', 'golint', 'errcheck'])
let l:default_enabled = ["vet", "golint", "errcheck"]

if go#config#MetalinterCommand() == "golangci-lint"
let l:default_enabled = ["govet", "golint"]
endif

return get(g:, "go_metalinter_enabled", default_enabled)
endfunction

function! go#config#MetalinterDisabled() abort
Expand Down
98 changes: 70 additions & 28 deletions autoload/go/lint.vim
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,14 @@ function! go#lint#Gometa(bang, autosave, ...) abort
let goargs = a:000
endif

if empty(go#config#MetalinterCommand())
let bin_path = go#path#CheckBinPath("gometalinter")
if empty(bin_path)
let l:metalinter = go#config#MetalinterCommand()

if l:metalinter == 'gometalinter' || l:metalinter == 'golangci-lint'
let cmd = s:metalintercmd(l:metalinter)
if empty(cmd)
return
endif

let cmd = [bin_path]
let cmd += ["--disable-all"]

" 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"]

" linters
let linters = a:autosave ? go#config#MetalinterAutosaveEnabled() : go#config#MetalinterEnabled()
for linter in linters
Expand All @@ -44,24 +36,41 @@ function! go#lint#Gometa(bang, autosave, ...) abort
" will be cleared
redraw

" Include only messages for the active buffer for autosave.
let include = [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
if go#util#has_job()
let include = [printf('--include=^%s:.*$', expand('%:p:t'))]
if l:metalinter == "gometalinter"
" Include only messages for the active buffer for autosave.
let include = [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
if go#util#has_job()
let include = [printf('--include=^%s:.*$', expand('%:p:t'))]
endif
let cmd += include
elseif l:metalinter == "golangci-lint"
let goargs[0] = expand('%:p')
endif
let cmd += include
endif

" Call gometalinter asynchronously.
" Call metalinter asynchronously.
let deadline = go#config#MetalinterDeadline()
if deadline != ''
let cmd += ["--deadline=" . deadline]
endif

let cmd += goargs

if l:metalinter == "gometalinter"
" Gometalinter can output one of the two, so we look for both:
" <file>:<line>:<column>:<severity>: <message> (<linter>)
" <file>:<line>::<severity>: <message> (<linter>)
" This can be defined by the following errorformat:
let errformat = "%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m"
else
" Golangci-lint can output the following:
" <file>:<line>:<column>: <message> (<linter>)
" This can be defined by the following errorformat:
let errformat = "%f:%l:%c:\ %m"
endif

if go#util#has_job()
call s:lint_job({'cmd': cmd}, a:bang, a:autosave)
call s:lint_job({'cmd': cmd, 'statustype': l:metalinter, 'errformat': errformat}, a:bang, a:autosave)
return
endif

Expand All @@ -77,12 +86,6 @@ function! go#lint#Gometa(bang, autosave, ...) abort
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>:<severity>: <message> (<linter>)
" <file>:<line>::<severity>: <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')

Expand Down Expand Up @@ -205,8 +208,8 @@ endfunction

function! s:lint_job(args, bang, autosave)
let l:opts = {
\ 'statustype': "gometalinter",
\ 'errorformat': '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m',
\ 'statustype': a:args.statustype,
\ 'errorformat': a:args.errformat,
\ 'for': "GoMetaLinter",
\ 'bang': a:bang,
\ }
Expand All @@ -221,6 +224,45 @@ function! s:lint_job(args, bang, autosave)
call go#job#Spawn(a:args.cmd, l:opts)
endfunction

function! s:metalintercmd(metalinter)
let l:cmd = []
let bin_path = go#path#CheckBinPath(a:metalinter)
if !empty(bin_path)
if a:metalinter == "gometalinter"
let l:cmd = s:gometalintercmd(bin_path)
elseif a:metalinter == "golangci-lint"
let l:cmd = s:golangcilintcmd(bin_path)
endif
endif

return cmd
endfunction

function! s:gometalintercmd(bin_path)
let cmd = [a:bin_path]
let cmd += ["--disable-all"]

" 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"]
return cmd
endfunction

function! s:golangcilintcmd(bin_path)
let cmd = [a:bin_path]
let cmd += ["run"]
let cmd += ["--print-issued-lines=false"]
let cmd += ["--disable-all"]
" do not use the default exclude patterns, because doing so causes golint
" problems about missing doc strings to be ignored and other things that
" golint identifies.
let cmd += ["--exclude-use-default=false"]
return cmd
endfunction

" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
Expand Down
130 changes: 83 additions & 47 deletions autoload/go/lint_test.vim
Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,122 @@ let s:cpo_save = &cpo
set cpo&vim

func! Test_Gometa() abort
call s:gometa('gometaliner')
endfunc

func! Test_GometaGolangciLint() abort
call s:gometa('golangci-lint')
endfunc

func! s:gometa(metalinter) abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'

let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
try
let g:go_metalinter_comand = a:metalinter
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]

" clear the quickfix lists
call setqflist([], 'r')
" clear the quickfix lists
call setqflist([], 'r')

let g:go_metalinter_enabled = ['golint']
let g:go_metalinter_enabled = ['golint']

call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')
call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')

let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile

call gotest#assert_quickfix(actual, expected)
unlet g:go_metalinter_enabled
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile

call gotest#assert_quickfix(actual, expected)
finally
unlet g:go_metalinter_enabled
endtry
endfunc

func! Test_GometaWithDisabled() abort
call s:gometawithdisabled('gometalinter')
endfunc

func! Test_GometaWithDisabledGolangciLint() abort
call s:gometawithdisabled('golangci-lint')
endfunc

func! s:gometawithdisabled(metalinter) abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'

let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]
try
let g:go_metalinter_comand = a:metalinter
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
\ ]

" clear the quickfix lists
call setqflist([], 'r')
" clear the quickfix lists
call setqflist([], 'r')

let g:go_metalinter_disabled = ['vet']
let g:go_metalinter_disabled = ['vet']

call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')
call go#lint#Gometa(0, 0, $GOPATH . '/src/foo')

let actual = getqflist()
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile

call gotest#assert_quickfix(actual, expected)
unlet g:go_metalinter_disabled
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getqflist()
endwhile

call gotest#assert_quickfix(actual, expected)
finally
unlet g:go_metalinter_disabled
endtry
endfunc

func! Test_GometaAutoSave() abort
call s:gometaautosave('gometalinter')
endfunc

func! Test_GometaAutoSaveGolangciLint() abort
call s:gometaautosave('golangci-lint')
endfunc

func! s:gometaautosave(metalinter) abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'

let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
\ ]
try
let g:go_metalinter_comand = a:metalinter
let expected = [
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
\ ]

let winnr = winnr()
let winnr = winnr()

" clear the location lists
call setloclist(l:winnr, [], 'r')
" clear the location lists
call setloclist(l:winnr, [], 'r')

let g:go_metalinter_autosave_enabled = ['golint']
let g:go_metalinter_autosave_enabled = ['golint']

call go#lint#Gometa(0, 1)
call go#lint#Gometa(0, 1)

let actual = getloclist(l:winnr)
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(l:winnr)
endwhile

call gotest#assert_quickfix(actual, expected)
unlet g:go_metalinter_autosave_enabled
let start = reltime()
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
sleep 100m
let actual = getloclist(l:winnr)
endwhile

call gotest#assert_quickfix(actual, expected)
finally
unlet g:go_metalinter_autosave_enabled
endtry
endfunc

func! Test_Vet()
func! Test_Vet() abort
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
compiler go
Expand Down
9 changes: 5 additions & 4 deletions doc/vim-go.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1515,11 +1515,12 @@ it's empty
<
*'g:go_metalinter_command'*

Overrides the command to be executed when |:GoMetaLinter| is called. This is
an advanced settings and is for users who want to have a complete control
over how `gometalinter` should be executed. By default it's empty.
Overrides the command to be executed when |:GoMetaLinter| is called. By
default it's `gometalinter`. `golangci-lint` is also supported. It can also be
used as an advanced setting for users who want to have more control over
the metalinter.
>
let g:go_metalinter_command = ""
let g:go_metalinter_command = "gometalinter"
<
*'g:go_metalinter_deadline'*

Expand Down
1 change: 1 addition & 0 deletions plugin/go.vim
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ let s:packages = {
\ 'golint': ['golang.org/x/lint/golint'],
\ 'gopls': ['golang.org/x/tools/cmd/gopls'],
\ 'gometalinter': ['github.com/alecthomas/gometalinter'],
\ 'golangci-lint': ['github.com/golangci/golangci-lint/cmd/golangci-lint'],
\ 'gomodifytags': ['github.com/fatih/gomodifytags'],
\ 'gorename': ['golang.org/x/tools/cmd/gorename'],
\ 'gotags': ['github.com/jstemmer/gotags'],
Expand Down

0 comments on commit 2774621

Please sign in to comment.