Skip to content

Commit

Permalink
lint: make gopls diagnostics visible
Browse files Browse the repository at this point in the history
Add a new command, `:GoDiagnostics`, to display the LSP diagnostics for
packages.

Teach :GoMetaLinter to display LSP diagnostics when
g:go_metalinter_command == 'gopls'.

Closes #2538
  • Loading branch information
bhcleek committed Dec 16, 2019
1 parent 9262f08 commit b095c56
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 77 deletions.
100 changes: 81 additions & 19 deletions autoload/go/lint.vim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function! go#lint#Gometa(bang, autosave, ...) abort

let l:metalinter = go#config#MetalinterCommand()

let cmd = []
if l:metalinter == 'golangci-lint'
let cmd = s:metalintercmd(l:metalinter)
if empty(cmd)
Expand All @@ -22,7 +23,7 @@ function! go#lint#Gometa(bang, autosave, ...) abort
for linter in linters
let cmd += ["--enable=".linter]
endfor
else
elseif l:metalinter != 'gopls'
" the user wants something else, let us use it.
let cmd = split(go#config#MetalinterCommand(), " ")
endif
Expand All @@ -45,17 +46,35 @@ function! go#lint#Gometa(bang, autosave, ...) abort

let cmd += goargs

" 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"
let errformat = s:errorformat(l:metalinter)

if go#util#has_job()
call s:lint_job({'cmd': cmd, 'statustype': l:metalinter, 'errformat': errformat}, a:bang, a:autosave)
return
endif
if l:metalinter == 'gopls'
if a:autosave
let l:messages = go#lsp#AnalyzeFile(expand('%:p'))
else
let l:import_paths = l:goargs
if len(l:import_paths) == 0
let l:pkg = go#package#ImportPath()
if l:pkg == -1
call go#util#EchoError('could not determine package name')
return
endif

let l:import_paths = [l:pkg]
endif
let l:messages = call('go#lsp#Diagnostics', l:import_paths)
endif

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

let [l:out, l:err] = go#util#Exec(cmd)
let [l:out, l:err] = go#util#Exec(cmd)
let l:messages = split(out, "\n")
endif

if a:autosave
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
Expand All @@ -70,9 +89,7 @@ function! go#lint#Gometa(bang, autosave, ...) abort
let l:winid = win_getid(winnr())
" Parse and populate our location list

let l:messages = split(out, "\n")

if a:autosave
if a:autosave && l:metalinter != 'gopls'
call s:metalinterautosavecomplete(fnamemodify(expand('%:p'), ":."), 0, 1, l:messages)
endif
call go#list#ParseFormat(l:listtype, errformat, l:messages, 'GoMetaLinter')
Expand All @@ -88,6 +105,44 @@ function! go#lint#Gometa(bang, autosave, ...) abort
endif
endfunction

function! go#lint#Diagnostics(bang, ...) abort
if a:0 == 0
let l:pkg = go#package#ImportPath()
if l:pkg == -1
call go#util#EchoError('could not determine package name')
return
endif

let l:import_paths = [l:pkg]
else
let l:import_paths = a:000
endif

let errformat = s:errorformat('gopls')

let l:messages = call('go#lsp#Diagnostics', l:import_paths)

let l:listtype = go#list#Type("GoDiagnostics")

if len(l:messages) == 0
call go#list#Clean(l:listtype)
call go#util#EchoSuccess('[diagnostics] PASS')
else
" Parse and populate the quickfix list
let l:winid = win_getid(winnr())
call go#list#ParseFormat(l:listtype, errformat, l:messages, 'GoDiagnostics')

let errors = go#list#Get(l:listtype)
call go#list#Window(l:listtype, len(errors))

if a:bang
call win_gotoid(l:winid)
return
endif
call go#list#JumpToFirst(l:listtype)
endif
endfunction

" Golint calls 'golint' on the current directory. Any warnings are populated in
" the location list
function! go#lint#Golint(bang, ...) abort
Expand Down Expand Up @@ -275,18 +330,25 @@ function! s:metalinterautosavecomplete(filepath, job, exit_code, messages)

let l:idx = len(a:messages) - 1
while l:idx >= 0
" Go 1.13 changed how go vet output is formatted by prepending a leading
" 'vet :', so account for that, too. This function isn't really needed for
" gometalinter at all, so the check for Go 1.13's go vet output shouldn't
" be neeeded, but s:lint_job hooks this up even when the
" g:go_metalinter_command is golangci-lint.
if a:messages[l:idx] !~# '^' . a:filepath . ':' && a:messages[l:idx] !~# '^vet: \.[\\/]' . a:filepath . ':'
if a:messages[l:idx] !~# '^' . a:filepath . ':'
call remove(a:messages, l:idx)
endif
let l:idx -= 1
endwhile
endfunction

function! s:errorformat(metalinter) abort
if a:metalinter == 'golangci-lint'
" Golangci-lint can output the following:
" <file>:<line>:<column>: <message> (<linter>)
" This can be defined by the following errorformat:
return '%f:%l:%c:\ %m'
elseif a:metalinter == 'gopls'
return '%f:%l:%c:%t:\ %m,%f:%l:%c::\ %m'
endif

endfunction

" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save
Expand Down
1 change: 1 addition & 0 deletions autoload/go/list.vim
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ endfunction
" in g:go_list_type_commands.
let s:default_list_type_commands = {
\ "GoBuild": "quickfix",
\ "GoDiagnostics": "quickfix",
\ "GoDebug": "quickfix",
\ "GoErrCheck": "quickfix",
\ "GoFmt": "locationlist",
Expand Down
Loading

0 comments on commit b095c56

Please sign in to comment.