Skip to content

Commit

Permalink
Merge pull request #2741 from woodstok/GoImplementsLsp
Browse files Browse the repository at this point in the history
Add support for gopls 'implements' functionality
  • Loading branch information
bhcleek authored Mar 29, 2020
2 parents 32b3d07 + 5e39e42 commit 2d9460e
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 6 deletions.
4 changes: 4 additions & 0 deletions autoload/go/config.vim
Original file line number Diff line number Diff line change
Expand Up @@ -507,6 +507,10 @@ function! go#config#ReferrersMode() abort
return get(g:, 'go_referrers_mode', 'gopls')
endfunction

function! go#config#ImplementsMode() abort
return get(g:, 'go_implements_mode', 'guru')
endfunction

function! go#config#GoplsCompleteUnimported() abort
return get(g:, 'go_gopls_complete_unimported', v:null)
endfunction
Expand Down
44 changes: 44 additions & 0 deletions autoload/go/implements.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
" don't spam the user when Vim is started in Vi compatibility mode
let s:cpo_save = &cpo
set cpo&vim

function! go#implements#Implements(selected) abort
let l:mode = go#config#ImplementsMode()
if l:mode == 'guru'
call go#guru#Implements(a:selected)
return
elseif l:mode == 'gopls'
if !go#config#GoplsEnabled()
call go#util#EchoError("go_implements_mode is 'gopls', but gopls is disabled")
endif
let [l:line, l:col] = getpos('.')[1:2]
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
let l:fname = expand('%:p')
call go#lsp#Implements(l:fname, l:line, l:col, funcref('s:parse_output'))
return
else
call go#util#EchoWarning('unknown value for g:go_implements_mode')
endif
endfunction

" This uses Vim's errorformat to parse the output and put it into a quickfix
" or locationlist.
function! s:parse_output(exit_val, output, title) abort
if a:exit_val
call go#util#EchoError(a:output)
return
endif

let errformat = ",%f:%l:%c:\ %m"
let l:listtype = go#list#Type("GoImplements")
call go#list#ParseFormat(l:listtype, errformat, a:output, a:title)

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

" restore Vi compatibility settings
let &cpo = s:cpo_save
unlet s:cpo_save

" vim: sw=2 ts=2 et
3 changes: 2 additions & 1 deletion autoload/go/list.vim
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ let s:default_list_type_commands = {
\ "GoRun": "quickfix",
\ "GoTest": "quickfix",
\ "GoVet": "quickfix",
\ "GoReferrers": "quickfix",
\ "GoReferrers": "locationlist",
\ "GoImplements": "locationlist",
\ "_guru": "locationlist",
\ "_term": "locationlist",
\ "_job": "locationlist",
Expand Down
36 changes: 34 additions & 2 deletions autoload/go/lsp.vim
Original file line number Diff line number Diff line change
Expand Up @@ -807,12 +807,16 @@ function! go#lsp#Referrers(fname, line, col, handler) abort

let l:state = s:newHandlerState('referrers')

let l:state.handleResult = funcref('s:referencesHandler', [function(a:handler, [], l:state)], l:state)
let l:state.handleResult = funcref('s:handleReferences', [function(a:handler, [], l:state)], l:state)
let l:state.error = funcref('s:noop')
return l:lsp.sendMessage(l:msg, l:state)
endfunction

function! s:referencesHandler(next, msg) abort dict
function! s:handleReferences(next, msg) abort dict
call s:handleLocations(a:next, a:msg)
endfunction

function! s:handleLocations(next, msg) abort
let l:result = []

let l:msg = a:msg
Expand Down Expand Up @@ -853,6 +857,34 @@ function! s:referencesHandler(next, msg) abort dict
call call(a:next, [0, l:result, ''])
endfunction

" go#lsp#Implementations calls gopls to get the implementations to the
" identifier at line and col in fname. handler should be a dictionary function
" that takes a list of strings in the form 'file:line:col: message'. handler
" will be attached to a dictionary that manages state (statuslines, sets the
" winid, etc.). handler should take three arguments: an exit_code, a JSON
" object encoded to a string that mimics guru's ouput for guru implements, and
" a third parameter that only exists for compatibility with guru implements.
function! go#lsp#Implements(fname, line, col, handler) abort
call go#lsp#DidChange(a:fname)

let l:lsp = s:lspfactory.get()
let l:msg = go#lsp#message#Implementation(a:fname, a:line, a:col)

let l:state = s:newHandlerState('implements')

let l:state.handleResult = funcref('s:handleImplements', [function(a:handler, [], l:state)], l:state)
let l:state.error = funcref('s:handleImplementsError', [function(a:handler, [], l:state)], l:state)
return l:lsp.sendMessage(l:msg, l:state)
endfunction

function! s:handleImplements(next, msg) abort dict
call s:handleLocations(a:next, a:msg)
endfunction

function! s:handleImplementsError(next, error) abort dict
call call(a:next, [1, [a:error], ''])
endfunction

function! go#lsp#Hover(fname, line, col, handler) abort
call go#lsp#DidChange(a:fname)

Expand Down
15 changes: 14 additions & 1 deletion autoload/go/lsp/message.vim
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ function! go#lsp#message#TypeDefinition(file, line, col) abort
\ }
endfunction

function! go#lsp#message#Implementation(file, line, col) abort
return {
\ 'notification': 0,
\ 'method': 'textDocument/implementation',
\ 'params': {
\ 'textDocument': {
\ 'uri': go#path#ToURI(a:file)
\ },
\ 'position': s:position(a:line, a:col)
\ }
\ }
endfunction

function! go#lsp#message#DidOpen(file, content, version) abort
return {
\ 'notification': 1,
Expand Down Expand Up @@ -189,7 +202,7 @@ endfunction

function! go#lsp#message#ChangeWorkspaceFolders(add, remove) abort
let l:addDirs = map(copy(a:add), function('s:workspaceFolder', []))
let l:removeDirs = map(copy(a:add), function('s:workspaceFolder', []))
let l:removeDirs = map(copy(a:remove), function('s:workspaceFolder', []))

return {
\ 'notification': 1,
Expand Down
10 changes: 9 additions & 1 deletion doc/vim-go.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1454,9 +1454,17 @@ Use this option to define the command to be used for |:GoReferrers|. By
default `gopls` is used, because it is the fastest and works with Go modules.
One might also use `guru` for its ability to show references from other
packages. This option will be removed after `gopls` can show references from
other packages. Valid options are `gopls` and `guru`.
other packages. Valid options are `gopls` and `guru`. By default it's `guru`.
>
let g:go_referrers_mode = 'gopls'
<
*'g:go_implements_mode'*

Use this option to define the command to be used for |:GoImplements|.
The Implements feature in gopls is still new and being worked upon.
Valid options are `gopls` and `guru`. By default it's `guru`.
>
let g:go_implements_mode = 'guru'
<
*'g:go_def_mapping_enabled'*

Expand Down
2 changes: 1 addition & 1 deletion ftplugin/go/commands.vim
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ command! -nargs=? -complete=customlist,go#rename#Complete GoRename call go#renam

" -- guru
command! -nargs=* -complete=customlist,go#package#Complete GoGuruScope call go#guru#Scope(<f-args>)
command! -range=% GoImplements call go#guru#Implements(<count>)
command! -range=% GoImplements call go#implements#Implements(<count>)
command! -range=% GoPointsTo call go#guru#PointsTo(<count>)
command! -range=% GoWhicherrs call go#guru#Whicherrs(<count>)
command! -range=% GoCallees call go#guru#Callees(<count>)
Expand Down

0 comments on commit 2d9460e

Please sign in to comment.