mirror of
https://github.com/amix/vimrc
synced 2025-06-29 02:55:01 +08:00
Updated plugins
This commit is contained in:
1
sources_non_forked/vim-go/.github/FUNDING.yml
vendored
Normal file
1
sources_non_forked/vim-go/.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
patreon: bhcleek
|
@ -6,10 +6,8 @@ If possible, please provide clear steps for reproducing the problem.
|
||||
|
||||
### What did you expect to happen?
|
||||
|
||||
|
||||
### What happened instead?
|
||||
|
||||
|
||||
### Configuration (**MUST** fill this out):
|
||||
|
||||
#### vim-go version:
|
||||
@ -20,10 +18,13 @@ If possible, please provide clear steps for reproducing the problem.
|
||||
</pre></details>
|
||||
|
||||
#### Vim version (first three lines from `:version`):
|
||||
<!-- :version -->
|
||||
|
||||
#### Go version (`go version`):
|
||||
#### Go version (`go version`):
|
||||
<!-- go version -->
|
||||
|
||||
#### Go environment
|
||||
<details><summary><code>go env</code> Output:</summary><br><pre>
|
||||
<!-- go env -->
|
||||
|
||||
</pre></details>
|
||||
|
@ -1,5 +1,9 @@
|
||||
## unplanned
|
||||
|
||||
BACKWARDS INCOMPATABILITIES:
|
||||
* `g:go_metalinter_disabled` has been removed.
|
||||
[[GH-2375]](https://github.com/fatih/vim-go/pull/2117)
|
||||
|
||||
IMPROVEMENTS:
|
||||
* Add a new option, `g:go_code_completion_enabled`, to control whether omnifunc
|
||||
is set.
|
||||
@ -8,6 +12,53 @@ IMPROVEMENTS:
|
||||
[[GH-2261]](https://github.com/fatih/vim-go/pull/2261)
|
||||
* Allow debugging of packages outside of GOPATH without a go.mod file.
|
||||
[[GH-2269]](https://github.com/fatih/vim-go/pull/2269)
|
||||
* Show which example failed when Example tests fail
|
||||
[[GH-2277]](https://github.com/fatih/vim-go/pull/2277)
|
||||
* Show function signature and return types in preview window when autocompleting functions and methods.
|
||||
[[GH-2289]](https://github.com/fatih/vim-go/pull/2289)
|
||||
* Improve the user experience when using null modules.
|
||||
[[GH-2300]](https://github.com/fatih/vim-go/pull/2300)
|
||||
* Modify `:GoReportGitHubIssue` to include vim-go configuration values
|
||||
[[GH-2323]](https://github.com/fatih/vim-go/pull/2323)
|
||||
* Respect `g:go_info_mode='gopls'` in go#complete#GetInfo.
|
||||
[[GH-2313]](https://github.com/fatih/vim-go/pull/2313)
|
||||
* Allow `:GoLint`, `:GoErrCheck`, and `:GoDebug` to work in null modules.
|
||||
[[GH-2335]](https://github.com/fatih/vim-go/pull/2335)
|
||||
* Change default value for `g:go_info_mode` and `g:go_def_mode` to `'gopls'`.
|
||||
[[GH-2329]](https://github.com/fatih/vim-go/pull/2329)
|
||||
* Add a new option, `g:go_doc_popup_window` to optionally use a popup window
|
||||
for godoc in Vim 8.1.1513 and later.
|
||||
[[GH-2347]](https://github.com/fatih/vim-go/pull/2347)
|
||||
* Add `:GoAddWorkspace` function to support multiple workspaces with gopls.
|
||||
[[GH-2356]](https://github.com/fatih/vim-go/pull/2356)
|
||||
* Install gopls from its stable package.
|
||||
[[GH-2360]](https://github.com/fatih/vim-go/pull/2360)
|
||||
* Disambiguate progress message when initializing gopls.
|
||||
[[GH-2369]](https://github.com/fatih/vim-go/pull/2369)
|
||||
* Calculate LSP position correctly when on a line that contains multi-byte
|
||||
characters before the position.
|
||||
[[GH-2389]](https://github.com/fatih/vim-go/pull/2389)
|
||||
* Calculate Vim position correctly from LSP text position.
|
||||
[[GH-2395]](https://github.com/fatih/vim-go/pull/2395)
|
||||
* Use the statusline to display gopls initialization status messages and only
|
||||
echo the statuses when `g:go_echo_command_info` is set.
|
||||
[[GH-2422]](https://github.com/fatih/vim-go/pull/2422)
|
||||
* Send configuration to gopls so that build tags will be considered and hover
|
||||
content won't have documentation.
|
||||
[[GH-2429]](https://github.com/fatih/vim-go/pull/2429)
|
||||
* Add a new option, `g:go_term_close_on_exit`, to control whether jobs run in a
|
||||
terminal window will close the terminal window when the job exits.
|
||||
[[GH-2409]](https://github.com/fatih/vim-go/pull/2409)
|
||||
* Allow `g:go_template_file` and `g:go_template_test_files` to reside outside
|
||||
of vim-go's template directory.
|
||||
[[GH-2434]](https://github.com/fatih/vim-go/pull/2434)
|
||||
* Add a new command, `:GoLSPDebugBrowser`, to open a browser to gopls debugging
|
||||
view.
|
||||
[[GH-2436]](https://github.com/fatih/vim-go/pull/2436)
|
||||
* Restart gopls automatically when it is updated via `:GoUpdateBinaries`.
|
||||
[[GH-2453]](https://github.com/fatih/vim-go/pull/2453)
|
||||
* Reset `'more'` while installing binaries to avoid unnecessary more prompts.
|
||||
[[GH-2457]](https://github.com/fatih/vim-go/pull/2457)
|
||||
|
||||
BUG FIXES:
|
||||
* display info about function and function types whose parameters are
|
||||
@ -26,6 +77,44 @@ BUG FIXES:
|
||||
[[GH-2268]](https://github.com/fatih/vim-go/pull/2268)
|
||||
* Set the anchor for method documentation correctly.
|
||||
[[GH-2276]](https://github.com/fatih/vim-go/pull/2276)
|
||||
* Respect the LSP information for determining where candidate matches start.
|
||||
[[GH-2291]](https://github.com/fatih/vim-go/pull/2291)
|
||||
* Restore environment variables with backslashes correctly.
|
||||
[[GH-2292]](https://github.com/fatih/vim-go/pull/2292)
|
||||
* Modify handling of gopls output for `:GoInfo` to ensure the value will be
|
||||
displayed.
|
||||
[[GH-2311]](https://github.com/fatih/vim-go/pull/2311)
|
||||
* Run `:GoLint` successfully in null modules.
|
||||
[[GH-2318]](https://github.com/fatih/vim-go/pull/2318)
|
||||
* Ensure actions on save work in new buffers that have not yet been persisted to disk.
|
||||
[[GH-2319]](https://github.com/fatih/vim-go/pull/2319)
|
||||
* Restore population of information in `:GoReportGitHubIssue`.
|
||||
[[GH-2312]](https://github.com/fatih/vim-go/pull/2312)
|
||||
* Do not jump back to the originating window when jumping to definitions with
|
||||
`g:go_def_mode='gopls'`.
|
||||
[[GH-2327]](https://github.com/fatih/vim-go/pull/2327)
|
||||
* Fix getting information about a valid identifier for which gopls returns no
|
||||
information (e.g. calling `:GoInfo` on a package identifier).
|
||||
[[GH-2339]](https://github.com/fatih/vim-go/pull/2339)
|
||||
* Fix tab completion of package names on the cmdline in null modules.
|
||||
[[GH-2342]](https://github.com/fatih/vim-go/pull/2342)
|
||||
* Display identifier info correctly when the identifier has no godoc.
|
||||
[[GH-2373]](https://github.com/fatih/vim-go/pull/2373)
|
||||
* Fix false positives when saving a buffer and `g:go_metalinter_command` is
|
||||
`golangci-lint`.
|
||||
[[GH-2367]](https://github.com/fatih/vim-go/pull/2367)
|
||||
* Fix `:GoDebugRestart`.
|
||||
[[GH-2390]](https://github.com/fatih/vim-go/pull/2390)
|
||||
* Do not execute tests twice in terminal mode.
|
||||
[[GH-2397]](https://github.com/fatih/vim-go/pull/2397)
|
||||
* Do not open a new buffer in Neovim when there are compilation errors and
|
||||
terminal mode is enabled.
|
||||
[[GH-2401]](https://github.com/fatih/vim-go/pull/2401)
|
||||
* Fix error due to typo in implementation of `:GoAddWorkspace`.
|
||||
[[GH-2415]](https://github.com/fatih/vim-go/pull/2401)
|
||||
* Do not format the file automatically when `g:go_format_autosave` is set and
|
||||
the file being written is not the current file.
|
||||
[[GH-2442]](https://github.com/fatih/vim-go/pull/2401)
|
||||
|
||||
## 1.20 - (April 22, 2019)
|
||||
|
||||
|
@ -68,7 +68,16 @@ Depending on your installation method, you may have to generate the plugin's
|
||||
[`help tags`](http://vimhelp.appspot.com/helphelp.txt.html#%3Ahelptags)
|
||||
manually (e.g. `:helptags ALL`).
|
||||
|
||||
We also have an [official vim-go tutorial](https://github.com/fatih/vim-go-tutorial).
|
||||
We also have an [official vim-go tutorial](https://github.com/fatih/vim-go/wiki).
|
||||
|
||||
## FAQ and troubleshooting
|
||||
|
||||
The FAQ and troubleshooting tips are in the documentation and can be quickly
|
||||
accessed using `:help go-troubleshooting`. If you believe you've found a bug or
|
||||
shortcoming in vim-go that is neither addressed by help nor in [existing
|
||||
issues](https://github.com/fatih/vim-go/issues), please open an issue with
|
||||
clear reproduction steps. `:GoReportGitHubIssue` can be used pre-populate a lot
|
||||
of the information needed when creating a new issue.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -33,7 +33,7 @@ function! go#auto#echo_go_info()
|
||||
endfunction
|
||||
|
||||
function! go#auto#auto_type_info()
|
||||
if !go#config#AutoTypeInfo() || !filereadable(expand('%:p'))
|
||||
if !go#config#AutoTypeInfo() || !isdirectory(expand('%:p:h'))
|
||||
return
|
||||
endif
|
||||
|
||||
@ -42,7 +42,7 @@ function! go#auto#auto_type_info()
|
||||
endfunction
|
||||
|
||||
function! go#auto#auto_sameids()
|
||||
if !go#config#AutoSameids() || !filereadable(expand('%:p'))
|
||||
if !go#config#AutoSameids() || !isdirectory(expand('%:p:h'))
|
||||
return
|
||||
endif
|
||||
|
||||
@ -51,7 +51,7 @@ function! go#auto#auto_sameids()
|
||||
endfunction
|
||||
|
||||
function! go#auto#fmt_autosave()
|
||||
if !go#config#FmtAutosave() || !filereadable(expand('%:p'))
|
||||
if !(go#config#FmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
|
||||
return
|
||||
endif
|
||||
|
||||
@ -60,7 +60,7 @@ function! go#auto#fmt_autosave()
|
||||
endfunction
|
||||
|
||||
function! go#auto#metalinter_autosave()
|
||||
if !go#config#MetalinterAutosave() || !filereadable(expand('%:p'))
|
||||
if !go#config#MetalinterAutosave() || !isdirectory(expand('%:p:h'))
|
||||
return
|
||||
endif
|
||||
|
||||
@ -69,7 +69,7 @@ function! go#auto#metalinter_autosave()
|
||||
endfunction
|
||||
|
||||
function! go#auto#modfmt_autosave()
|
||||
if !go#config#ModFmtAutosave() || !filereadable(expand('%:p'))
|
||||
if !(go#config#ModFmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
|
||||
return
|
||||
endif
|
||||
|
||||
@ -78,7 +78,7 @@ function! go#auto#modfmt_autosave()
|
||||
endfunction
|
||||
|
||||
function! go#auto#asmfmt_autosave()
|
||||
if !go#config#AsmfmtAutosave() || !filereadable(expand('%:p'))
|
||||
if !(go#config#AsmfmtAutosave() && isdirectory(expand('%:p:h')) && expand('<afile>:p') == expand('%:p'))
|
||||
return
|
||||
endif
|
||||
|
||||
|
@ -9,8 +9,8 @@ function! go#cmd#autowrite() abort
|
||||
for l:nr in range(0, bufnr('$'))
|
||||
if buflisted(l:nr) && getbufvar(l:nr, '&modified')
|
||||
" Sleep one second to make sure people see the message. Otherwise it is
|
||||
" often immediacy overwritten by the async messages (which also don't
|
||||
" invoke the "hit ENTER" prompt).
|
||||
" often immediately overwritten by the async messages (which also
|
||||
" doesn't invoke the "hit ENTER" prompt).
|
||||
call go#util#EchoWarning('[No write since last change]')
|
||||
sleep 1
|
||||
return
|
||||
|
@ -79,7 +79,12 @@ endfunction
|
||||
" go#complete#GoInfo returns the description of the identifier under the
|
||||
" cursor.
|
||||
function! go#complete#GetInfo() abort
|
||||
return s:sync_info(0)
|
||||
let l:mode = go#config#InfoMode()
|
||||
if l:mode == 'gopls' && go#util#has_job()
|
||||
return go#lsp#GetInfo()
|
||||
else
|
||||
return s:sync_info(0)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! go#complete#Info(showstatus) abort
|
||||
@ -216,6 +221,7 @@ function! s:info_complete(echo, result) abort
|
||||
endfunction
|
||||
|
||||
function! s:trim_bracket(val) abort
|
||||
echom a:val
|
||||
let a:val.word = substitute(a:val.word, '[(){}\[\]]\+$', '', '')
|
||||
return a:val
|
||||
endfunction
|
||||
@ -240,37 +246,44 @@ function! go#complete#GocodeComplete(findstart, base) abort
|
||||
else
|
||||
let s = getline(".")[col('.') - 1]
|
||||
if s =~ '[(){}\{\}]'
|
||||
return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
|
||||
return map(copy(s:completions), 's:trim_bracket(v:val)')
|
||||
endif
|
||||
return s:completions
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! go#complete#Complete(findstart, base) abort
|
||||
let l:state = {'done': 0, 'matches': []}
|
||||
let l:state = {'done': 0, 'matches': [], 'start': -1}
|
||||
|
||||
function! s:handler(state, matches) abort dict
|
||||
function! s:handler(state, start, matches) abort dict
|
||||
let a:state.start = a:start
|
||||
let a:state.matches = a:matches
|
||||
let a:state.done = 1
|
||||
endfunction
|
||||
|
||||
"findstart = 1 when we need to get the start of the match
|
||||
if a:findstart == 1
|
||||
call go#lsp#Completion(expand('%:p'), line('.'), col('.'), funcref('s:handler', [l:state]))
|
||||
let [l:line, l:col] = getpos('.')[1:2]
|
||||
let [l:line, l:col] = go#lsp#lsp#Position(l:line, l:col)
|
||||
let l:completion = go#lsp#Completion(expand('%:p'), l:line, l:col, funcref('s:handler', [l:state]))
|
||||
if l:completion
|
||||
return -3
|
||||
endif
|
||||
|
||||
while !l:state.done
|
||||
sleep 10m
|
||||
endwhile
|
||||
|
||||
let s:completions = l:state.matches
|
||||
|
||||
if len(l:state.matches) == 0
|
||||
" no matches. cancel and leave completion mode.
|
||||
call go#util#EchoInfo("no matches")
|
||||
return -3
|
||||
endif
|
||||
|
||||
return col('.')
|
||||
let s:completions = l:state.matches
|
||||
|
||||
return go#lsp#lsp#PositionOf(getline(l:line+1), l:state.start-1)
|
||||
|
||||
else "findstart = 0 when we need to return the list of completions
|
||||
return s:completions
|
||||
endif
|
||||
|
@ -2,23 +2,33 @@
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
func! Test_GetInfo()
|
||||
func! Test_GetInfo_gocode()
|
||||
let g:go_info_mode = 'gocode'
|
||||
call s:getinfo()
|
||||
unlet g:go_info_mode
|
||||
endfunction
|
||||
|
||||
func! Test_GetInfo_guru()
|
||||
let g:go_info_mode = 'guru'
|
||||
call s:getinfo()
|
||||
unlet g:go_info_mode
|
||||
endfunction
|
||||
|
||||
func! Test_GetInfo_gopls()
|
||||
let g:go_info_mode = 'gopls'
|
||||
call s:getinfo()
|
||||
unlet g:go_info_mode
|
||||
endfunction
|
||||
|
||||
func! s:getinfo()
|
||||
let l:filename = 'complete/complete.go'
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
|
||||
call cursor(8, 3)
|
||||
|
||||
let g:go_info_mode = 'gocode'
|
||||
let expected = 'func Example(s string)'
|
||||
let actual = go#complete#GetInfo()
|
||||
call assert_equal(expected, actual)
|
||||
|
||||
let g:go_info_mode = 'guru'
|
||||
call go#config#InfoMode()
|
||||
let actual = go#complete#GetInfo()
|
||||
call assert_equal(expected, actual)
|
||||
|
||||
unlet g:go_info_mode
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
|
@ -21,10 +21,12 @@ endfunction
|
||||
function! go#config#SetBuildTags(value) abort
|
||||
if a:value is ''
|
||||
silent! unlet g:go_build_tags
|
||||
call go#lsp#ResetWorkspaceDirectories()
|
||||
return
|
||||
endif
|
||||
|
||||
let g:go_build_tags = a:value
|
||||
call go#lsp#ResetWorkspaceDirectories()
|
||||
endfunction
|
||||
|
||||
function! go#config#TestTimeout() abort
|
||||
@ -47,6 +49,14 @@ function! go#config#TermMode() abort
|
||||
return get(g:, 'go_term_mode', 'vsplit')
|
||||
endfunction
|
||||
|
||||
function! go#config#TermCloseOnExit() abort
|
||||
return get(g:, 'go_term_close_on_exit', 1)
|
||||
endfunction
|
||||
|
||||
function! go#config#SetTermCloseOnExit(value) abort
|
||||
let g:go_term_close_on_exit = a:value
|
||||
endfunction
|
||||
|
||||
function! go#config#TermEnabled() abort
|
||||
return has('nvim') && get(g:, 'go_term_enabled', 0)
|
||||
endfunction
|
||||
@ -114,7 +124,7 @@ function! go#config#ListAutoclose() abort
|
||||
endfunction
|
||||
|
||||
function! go#config#InfoMode() abort
|
||||
return get(g:, 'go_info_mode', 'gocode')
|
||||
return get(g:, 'go_info_mode', 'gopls')
|
||||
endfunction
|
||||
|
||||
function! go#config#GuruScope() abort
|
||||
@ -174,12 +184,15 @@ function! go#config#DocUrl() abort
|
||||
return godoc_url
|
||||
endfunction
|
||||
|
||||
function! go#config#DocPopupWindow() abort
|
||||
return get(g:, 'go_doc_popup_window', 0)
|
||||
endfunction
|
||||
function! go#config#DefReuseBuffer() abort
|
||||
return get(g:, 'go_def_reuse_buffer', 0)
|
||||
endfunction
|
||||
|
||||
function! go#config#DefMode() abort
|
||||
return get(g:, 'go_def_mode', 'guru')
|
||||
return get(g:, 'go_def_mode', 'gopls')
|
||||
endfunction
|
||||
|
||||
function! go#config#DeclsIncludes() abort
|
||||
@ -268,10 +281,6 @@ function! go#config#MetalinterEnabled() abort
|
||||
return get(g:, "go_metalinter_enabled", default_enabled)
|
||||
endfunction
|
||||
|
||||
function! go#config#MetalinterDisabled() abort
|
||||
return get(g:, "go_metalinter_disabled", [])
|
||||
endfunction
|
||||
|
||||
function! go#config#GolintBin() abort
|
||||
return get(g:, "go_golint_bin", "golint")
|
||||
endfunction
|
||||
|
@ -576,7 +576,7 @@ function! go#debug#Start(is_test, ...) abort
|
||||
return s:state['job']
|
||||
endif
|
||||
|
||||
let s:start_args = a:000
|
||||
let s:start_args = [a:is_test] + a:000
|
||||
|
||||
if go#util#HasDebug('debugger-state')
|
||||
call go#config#SetDebugDiag(s:state)
|
||||
@ -595,10 +595,23 @@ function! go#debug#Start(is_test, ...) abort
|
||||
|
||||
" append the package when it's given.
|
||||
if len(a:000) > 0
|
||||
let l:pkgname = go#package#FromPath(a:1)
|
||||
if l:pkgname is -1
|
||||
call go#util#EchoError('could not determine package name')
|
||||
return
|
||||
let l:pkgname = a:1
|
||||
if l:pkgname[0] == '.'
|
||||
let l:pkgabspath = fnamemodify(l:pkgname, ':p')
|
||||
|
||||
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||
let l:dir = getcwd()
|
||||
execute l:cd fnameescape(expand('%:p:h'))
|
||||
|
||||
try
|
||||
let l:pkgname = go#package#FromPath(l:pkgabspath)
|
||||
if type(l:pkgname) == type(0)
|
||||
call go#util#EchoError('could not determine package name')
|
||||
return
|
||||
endif
|
||||
finally
|
||||
execute l:cd fnameescape(l:dir)
|
||||
endtry
|
||||
endif
|
||||
|
||||
let l:cmd += [l:pkgname]
|
||||
|
@ -10,6 +10,10 @@ function! Test_GoDebugStart_RelativePackage() abort
|
||||
call s:debug('./debug/debugmain')
|
||||
endfunction
|
||||
|
||||
function! Test_GoDebugStart_RelativePackage_NullModule() abort
|
||||
call s:debug('./debug/debugmain', 1)
|
||||
endfunction
|
||||
|
||||
function! Test_GoDebugStart_Package() abort
|
||||
call s:debug('debug/debugmain')
|
||||
endfunction
|
||||
@ -52,14 +56,22 @@ function! Test_GoDebugStart_Errors() abort
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" s:debug takes 2 optional arguments. The first is a package to debug. The
|
||||
" second is a flag to indicate whether to reset GOPATH after
|
||||
" gotest#load_fixture is called in order to test behavior outside of GOPATH.
|
||||
function! s:debug(...) abort
|
||||
if !go#util#has_job()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let $oldgopath = $GOPATH
|
||||
let l:tmp = gotest#load_fixture('debug/debugmain/debugmain.go')
|
||||
|
||||
if a:0 > 1 && a:2 == 1
|
||||
let $GOPATH = $oldgopath
|
||||
endif
|
||||
|
||||
call go#debug#Breakpoint(6)
|
||||
|
||||
call assert_false(exists(':GoDebugStop'))
|
||||
|
@ -6,7 +6,7 @@ let s:go_stack = []
|
||||
let s:go_stack_level = 0
|
||||
|
||||
function! go#def#Jump(mode, type) abort
|
||||
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
||||
let l:fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
|
||||
|
||||
" so guru right now is slow for some people. previously we were using
|
||||
" godef which also has it's own quirks. But this issue come up so many
|
||||
@ -66,7 +66,7 @@ function! go#def#Jump(mode, type) abort
|
||||
let [l:out, l:err] = go#util#ExecInDir(l:cmd)
|
||||
endif
|
||||
elseif bin_name == 'gopls'
|
||||
let [l:line, l:col] = getpos('.')[1:2]
|
||||
let [l:line, l:col] = go#lsp#lsp#Position()
|
||||
" delegate to gopls, with an empty job object and an exit status of 0
|
||||
" (they're irrelevant for gopls).
|
||||
if a:type
|
||||
|
@ -2,19 +2,21 @@
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
func! Test_jump_to_declaration_guru() abort
|
||||
try
|
||||
let l:filename = 'def/jump.go'
|
||||
let lnum = 5
|
||||
let col = 6
|
||||
let l:lnum = 5
|
||||
let l:col = 6
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
|
||||
let guru_out = printf("%s:%d:%d: defined here as func main", filename, lnum, col)
|
||||
call go#def#jump_to_declaration(guru_out, "", 'guru')
|
||||
let l:guru_out = printf("%s:%d:%d: defined here as func main", l:filename, l:lnum, l:col)
|
||||
call go#def#jump_to_declaration(l:guru_out, "", 'guru')
|
||||
|
||||
call assert_equal(filename, bufname("%"))
|
||||
call assert_equal(lnum, getcurpos()[1])
|
||||
call assert_equal(col, getcurpos()[2])
|
||||
call assert_equal(l:filename, bufname("%"))
|
||||
call assert_equal(l:lnum, getcurpos()[1])
|
||||
call assert_equal(l:col, getcurpos()[2])
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
@ -22,17 +24,17 @@ endfunc
|
||||
|
||||
func! Test_jump_to_declaration_godef() abort
|
||||
try
|
||||
let filename = 'def/jump.go'
|
||||
let lnum = 5
|
||||
let col = 6
|
||||
let l:filename = 'def/jump.go'
|
||||
let l:lnum = 5
|
||||
let l:col = 6
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
|
||||
let godef_out = printf("%s:%d:%d\ndefined here as func main", filename, lnum, col)
|
||||
let l:godef_out = printf("%s:%d:%d\ndefined here as func main", l:filename, l:lnum, l:col)
|
||||
call go#def#jump_to_declaration(godef_out, "", 'godef')
|
||||
|
||||
call assert_equal(filename, bufname("%"))
|
||||
call assert_equal(lnum, getcurpos()[1])
|
||||
call assert_equal(col, getcurpos()[2])
|
||||
call assert_equal(l:filename, bufname("%"))
|
||||
call assert_equal(l:lnum, getcurpos()[1])
|
||||
call assert_equal(l:col, getcurpos()[2])
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
@ -40,33 +42,180 @@ endfunc
|
||||
|
||||
func! Test_Jump_leaves_lists() abort
|
||||
try
|
||||
let filename = 'def/jump.go'
|
||||
let l:filename = 'def/jump.go'
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
|
||||
let expected = [{'lnum': 10, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'quux'}]
|
||||
let l:expected = [{'lnum': 10, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'quux'}]
|
||||
|
||||
call setloclist(winnr(), copy(expected), 'r' )
|
||||
call setqflist(copy(expected), 'r' )
|
||||
call setloclist(winnr(), copy(l:expected), 'r' )
|
||||
call setqflist(copy(l:expected), 'r' )
|
||||
|
||||
let l:bufnr = bufnr('%')
|
||||
call cursor(6, 7)
|
||||
|
||||
if !go#util#has_job()
|
||||
let g:go_def_mode='godef'
|
||||
endif
|
||||
call go#def#Jump('', 0)
|
||||
|
||||
let start = reltime()
|
||||
while bufnr('%') == l:bufnr && reltimefloat(reltime(start)) < 10
|
||||
if !go#util#has_job()
|
||||
unlet g:go_def_mode
|
||||
endif
|
||||
|
||||
let l:start = reltime()
|
||||
while bufnr('%') == l:bufnr && reltimefloat(reltime(l:start)) < 10
|
||||
sleep 100m
|
||||
endwhile
|
||||
|
||||
let actual = getloclist(winnr())
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
let l:actual = getloclist(winnr())
|
||||
call gotest#assert_quickfix(l:actual, l:expected)
|
||||
|
||||
let actual = getqflist()
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
let l:actual = getqflist()
|
||||
call gotest#assert_quickfix(l:actual, l:expected)
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_DefJump_gopls_simple_first() abort
|
||||
if !go#util#has_job()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let g:go_def_mode = 'gopls'
|
||||
|
||||
let l:tmp = gotest#write_file('simple/firstposition/firstposition.go', [
|
||||
\ 'package firstposition',
|
||||
\ '',
|
||||
\ 'func Example() {',
|
||||
\ "\tid := " . '"foo"',
|
||||
\ "\tprintln(" . '"id:", id)',
|
||||
\ '}',
|
||||
\ ] )
|
||||
|
||||
let l:expected = [0, 4, 2, 0]
|
||||
|
||||
call assert_notequal(l:expected, getpos('.'))
|
||||
|
||||
call go#def#Jump('', 0)
|
||||
|
||||
let l:start = reltime()
|
||||
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
|
||||
sleep 100m
|
||||
endwhile
|
||||
|
||||
call assert_equal(l:expected, getpos('.'))
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
unlet g:go_def_mode
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_DefJump_gopls_simple_last() abort
|
||||
if !go#util#has_job()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let g:go_def_mode = 'gopls'
|
||||
|
||||
let l:tmp = gotest#write_file('simple/lastposition/lastposition.go', [
|
||||
\ 'package lastposition',
|
||||
\ '',
|
||||
\ 'func Example() {',
|
||||
\ "\tid := " . '"foo"',
|
||||
\ "\tprintln(" . '"id:", id)',
|
||||
\ '}',
|
||||
\ ] )
|
||||
|
||||
let l:expected = [0, 4, 2, 0]
|
||||
|
||||
call assert_notequal(l:expected, getpos('.'))
|
||||
|
||||
call go#def#Jump('', 0)
|
||||
|
||||
let l:start = reltime()
|
||||
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
|
||||
sleep 100m
|
||||
endwhile
|
||||
|
||||
call assert_equal(l:expected, getpos('.'))
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
unlet g:go_def_mode
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_DefJump_gopls_MultipleCodeUnit_first() abort
|
||||
if !go#util#has_job()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let g:go_def_mode = 'gopls'
|
||||
|
||||
let l:tmp = gotest#write_file('multiplecodeunit/firstposition/firstposition.go', [
|
||||
\ 'package firstposition',
|
||||
\ '',
|
||||
\ 'func Example() {',
|
||||
\ "\t𐐀, id := " . '"foo", "bar"',
|
||||
\ "\tprintln(" . '"(𐐀, id):", 𐐀, id)',
|
||||
\ '}',
|
||||
\ ] )
|
||||
|
||||
let l:expected = [0, 4, 8, 0]
|
||||
call assert_notequal(l:expected, getpos('.'))
|
||||
|
||||
call go#def#Jump('', 0)
|
||||
|
||||
let l:start = reltime()
|
||||
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
|
||||
sleep 100m
|
||||
endwhile
|
||||
|
||||
call assert_equal(l:expected, getpos('.'))
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
unlet g:go_def_mode
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
|
||||
func! Test_DefJump_gopls_MultipleCodeUnit_last() abort
|
||||
if !go#util#has_job()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let g:go_def_mode = 'gopls'
|
||||
|
||||
let l:tmp = gotest#write_file('multiplecodeunit/lastposition/lastposition.go', [
|
||||
\ 'package lastposition',
|
||||
\ '',
|
||||
\ 'func Example() {',
|
||||
\ "\t𐐀, id := " . '"foo", "bar"',
|
||||
\ "\tprintln(" . '"(𐐀, id):", 𐐀, id)',
|
||||
\ '}',
|
||||
\ ] )
|
||||
|
||||
let l:expected = [0, 4, 8, 0]
|
||||
call assert_notequal(l:expected, getpos('.'))
|
||||
|
||||
call go#def#Jump('', 0)
|
||||
|
||||
let l:start = reltime()
|
||||
while getpos('.') != l:expected && reltimefloat(reltime(l:start)) < 10
|
||||
sleep 100m
|
||||
endwhile
|
||||
|
||||
call assert_equal(l:expected, getpos('.'))
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
unlet g:go_def_mode
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
@ -76,6 +76,18 @@ function! go#doc#Open(newmode, mode, ...) abort
|
||||
endfunction
|
||||
|
||||
function! s:GodocView(newposition, position, content) abort
|
||||
" popup window
|
||||
if go#config#DocPopupWindow() && exists('*popup_atcursor') && exists('*popup_clear')
|
||||
call popup_clear()
|
||||
|
||||
call popup_atcursor(split(a:content, '\n'), {
|
||||
\ 'padding': [1, 1, 1, 1],
|
||||
\ 'borderchars': ['-','|','-','|','+','+','+','+'],
|
||||
\ "border": [1, 1, 1, 1],
|
||||
\ })
|
||||
return
|
||||
endif
|
||||
|
||||
" reuse existing buffer window if it exists otherwise create a new one
|
||||
let is_visible = bufexists(s:buf_nr) && bufwinnr(s:buf_nr) != -1
|
||||
if !bufexists(s:buf_nr)
|
||||
|
@ -18,20 +18,30 @@ function! s:issuebody() abort
|
||||
for l in lines
|
||||
let body = add(body, l)
|
||||
|
||||
if l =~ '^\* Vim version'
|
||||
if l =~ '^<!-- :version'
|
||||
redir => out
|
||||
silent version
|
||||
redir END
|
||||
let body = extend(body, split(out, "\n")[0:2])
|
||||
elseif l =~ '^\* Go version'
|
||||
elseif l =~ '^<!-- go version -->'
|
||||
let [out, err] = go#util#Exec(['go', 'version'])
|
||||
let body = add(body, substitute(l:out, rtrimpat, '', ''))
|
||||
elseif l =~ '^\* Go environment'
|
||||
elseif l =~ '^<!-- go env -->'
|
||||
let [out, err] = go#util#Exec(['go', 'env'])
|
||||
let body = add(body, substitute(l:out, rtrimpat, '', ''))
|
||||
endif
|
||||
endfor
|
||||
|
||||
let body = add(body, "#### vim-go configuration:\n<details><summary>vim-go configuration</summary><br><pre>")
|
||||
|
||||
for k in keys(g:)
|
||||
if k =~ '^go_'
|
||||
let body = add(body, 'g:' . k . ' = ' . string(get(g:, k)))
|
||||
endif
|
||||
endfor
|
||||
|
||||
let body = add(body, '</pre></details>')
|
||||
|
||||
return join(body, "\n")
|
||||
endfunction
|
||||
|
||||
|
@ -22,10 +22,6 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
for linter in linters
|
||||
let cmd += ["--enable=".linter]
|
||||
endfor
|
||||
|
||||
for linter in go#config#MetalinterDisabled()
|
||||
let cmd += ["--disable=".linter]
|
||||
endfor
|
||||
else
|
||||
" the user wants something else, let us use it.
|
||||
let cmd = split(go#config#MetalinterCommand(), " ")
|
||||
@ -44,7 +40,7 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
endif
|
||||
let cmd += include
|
||||
elseif l:metalinter == "golangci-lint"
|
||||
let goargs[0] = expand('%:p')
|
||||
let goargs[0] = expand('%:p:h')
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -88,7 +84,13 @@ function! go#lint#Gometa(bang, autosave, ...) abort
|
||||
else
|
||||
let l:winid = win_getid(winnr())
|
||||
" Parse and populate our location list
|
||||
call go#list#ParseFormat(l:listtype, errformat, split(out, "\n"), 'GoMetaLinter')
|
||||
|
||||
let l:messages = split(out, "\n")
|
||||
|
||||
if a:autosave
|
||||
call s:metalinterautosavecomplete(fnamemodify(expand('%:p'), ":."), 0, 1, l:messages)
|
||||
endif
|
||||
call go#list#ParseFormat(l:listtype, errformat, l:messages, 'GoMetaLinter')
|
||||
|
||||
let errors = go#list#Get(l:listtype)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
@ -105,7 +107,7 @@ endfunction
|
||||
" the location list
|
||||
function! go#lint#Golint(bang, ...) abort
|
||||
if a:0 == 0
|
||||
let [l:out, l:err] = go#util#Exec([go#config#GolintBin(), go#package#ImportPath()])
|
||||
let [l:out, l:err] = go#util#Exec([go#config#GolintBin(), expand('%:p:h')])
|
||||
else
|
||||
let [l:out, l:err] = go#util#Exec([go#config#GolintBin()] + a:000)
|
||||
endif
|
||||
@ -141,7 +143,7 @@ function! go#lint#Vet(bang, ...) abort
|
||||
if a:0 == 0
|
||||
let [l:out, l:err] = go#util#Exec(['go', 'vet', go#package#ImportPath()])
|
||||
else
|
||||
let [l:out, l:err] = go#util#Exec(['go', 'tool', 'vet'] + a:000)
|
||||
let [l:out, l:err] = go#util#ExecInDir(['go', 'tool', 'vet'] + a:000)
|
||||
endif
|
||||
|
||||
let l:listtype = go#list#Type("GoVet")
|
||||
@ -230,6 +232,7 @@ function! s:lint_job(args, bang, autosave)
|
||||
|
||||
if a:autosave
|
||||
let l:opts.for = "GoMetaLinterAutoSave"
|
||||
let l:opts.complete = funcref('s:metalinterautosavecomplete', [expand('%:p:t')])
|
||||
endif
|
||||
|
||||
" autowrite is not enabled for jobs
|
||||
@ -279,6 +282,21 @@ function! s:golangcilintcmd(bin_path)
|
||||
return cmd
|
||||
endfunction
|
||||
|
||||
function! s:metalinterautosavecomplete(filepath, job, exit_code, messages)
|
||||
if len(a:messages) == 0
|
||||
return
|
||||
endif
|
||||
|
||||
let l:file = expand('%:p:t')
|
||||
let l:idx = len(a:messages) - 1
|
||||
while l:idx >= 0
|
||||
if a:messages[l:idx] !~# '^' . a:filepath . ':'
|
||||
call remove(a:messages, l:idx)
|
||||
endif
|
||||
let l:idx -= 1
|
||||
endwhile
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
@ -3,7 +3,7 @@ let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
func! Test_Gometa() abort
|
||||
call s:gometa('gometaliner')
|
||||
call s:gometa('gometalinter')
|
||||
endfunc
|
||||
|
||||
func! Test_GometaGolangciLint() abort
|
||||
@ -11,14 +11,19 @@ func! Test_GometaGolangciLint() abort
|
||||
endfunc
|
||||
|
||||
func! s:gometa(metalinter) abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnamemodify(getcwd(), ':p') . 'test-fixtures/lint')
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
try
|
||||
let g:go_metalinter_comand = a:metalinter
|
||||
let g:go_metalinter_command = 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)'}
|
||||
\ ]
|
||||
if a:metalinter == 'golangci-lint'
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function `MissingFooDoc` should have comment or be unexported (golint)'}
|
||||
\ ]
|
||||
endif
|
||||
|
||||
" clear the quickfix lists
|
||||
call setqflist([], 'r')
|
||||
@ -36,48 +41,11 @@ func! s:gometa(metalinter) abort
|
||||
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
finally
|
||||
call call(RestoreGOPATH, [])
|
||||
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'
|
||||
|
||||
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')
|
||||
|
||||
let g:go_metalinter_disabled = ['vet']
|
||||
|
||||
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)
|
||||
finally
|
||||
unlet g:go_metalinter_disabled
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_GometaAutoSave() abort
|
||||
call s:gometaautosave('gometalinter')
|
||||
endfunc
|
||||
@ -87,14 +55,19 @@ func! Test_GometaAutoSaveGolangciLint() abort
|
||||
endfunc
|
||||
|
||||
func! s:gometaautosave(metalinter) abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
try
|
||||
let g:go_metalinter_comand = a:metalinter
|
||||
let g:go_metalinter_command = 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)'}
|
||||
\ ]
|
||||
if a:metalinter == 'golangci-lint'
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function `MissingDoc` should have comment or be unexported (golint)'}
|
||||
\ ]
|
||||
endif
|
||||
|
||||
let winnr = winnr()
|
||||
|
||||
@ -114,18 +87,50 @@ func! s:gometaautosave(metalinter) abort
|
||||
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
finally
|
||||
call call(RestoreGOPATH, [])
|
||||
unlet g:go_metalinter_autosave_enabled
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_Vet() abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
|
||||
let l:tmp = gotest#load_fixture('lint/src/vet/vet.go')
|
||||
|
||||
try
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '',
|
||||
\ 'text': 'Printf format %d has arg str of wrong type string'}
|
||||
\ ]
|
||||
|
||||
let winnr = winnr()
|
||||
|
||||
" clear the location lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
call go#lint#Vet(1)
|
||||
|
||||
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)
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_Lint_GOPATH() abort
|
||||
let RestoreGOPATH = go#util#SetEnv('GOPATH', fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint')
|
||||
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
compiler go
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '',
|
||||
\ 'text': 'Printf format %d has arg str of wrong type string'}
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported'},
|
||||
\ {'lnum': 5, 'bufnr': 6, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function AlsoMissingDoc should have comment or be unexported'}
|
||||
\ ]
|
||||
|
||||
let winnr = winnr()
|
||||
@ -133,7 +138,35 @@ func! Test_Vet() abort
|
||||
" clear the location lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
call go#lint#Vet(1)
|
||||
call go#lint#Golint(1)
|
||||
|
||||
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)
|
||||
|
||||
call call(RestoreGOPATH, [])
|
||||
endfunc
|
||||
|
||||
func! Test_Lint_NullModule() abort
|
||||
silent exe 'e ' . fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint/src/lint/lint.go'
|
||||
compiler go
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported'},
|
||||
\ {'lnum': 5, 'bufnr': 6, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'exported function AlsoMissingDoc should have comment or be unexported'}
|
||||
\ ]
|
||||
|
||||
let winnr = winnr()
|
||||
|
||||
" clear the location lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
call go#lint#Golint(1)
|
||||
|
||||
let actual = getqflist()
|
||||
let start = reltime()
|
||||
|
@ -7,7 +7,7 @@ scriptencoding utf-8
|
||||
let s:lspfactory = {}
|
||||
|
||||
function! s:lspfactory.get() dict abort
|
||||
if !has_key(self, 'current') || empty(self.current)
|
||||
if empty(get(self, 'current', {})) || empty(get(self.current, 'job', {}))
|
||||
let self.current = s:newlsp()
|
||||
endif
|
||||
|
||||
@ -22,9 +22,16 @@ endfunction
|
||||
|
||||
function! s:newlsp() abort
|
||||
if !go#util#has_job()
|
||||
" TODO(bc): start the server in the background using a shell that waits for the right output before returning.
|
||||
call go#util#EchoError('This feature requires either Vim 8.0.0087 or newer with +job or Neovim.')
|
||||
return
|
||||
let l:oldshortmess=&shortmess
|
||||
if has('nvim')
|
||||
set shortmess-=F
|
||||
endif
|
||||
call go#util#EchoWarning('Features that rely on gopls will not work without either Vim 8.0.0087 or newer with +job or Neovim')
|
||||
" Sleep one second to make sure people see the message. Otherwise it is
|
||||
" often immediately overwritten by an async message.
|
||||
sleep 1
|
||||
let &shortmess=l:oldshortmess
|
||||
return {'sendMessage': funcref('s:noop')}
|
||||
endif
|
||||
|
||||
" job is the job used to talk to the backing instance of gopls.
|
||||
@ -47,6 +54,8 @@ function! s:newlsp() abort
|
||||
\ 'last_request_id': 0,
|
||||
\ 'buf': '',
|
||||
\ 'handlers': {},
|
||||
\ 'workspaceDirectories': [],
|
||||
\ 'wd' : '',
|
||||
\ }
|
||||
|
||||
function! l:lsp.readMessage(data) dict abort
|
||||
@ -75,19 +84,17 @@ function! s:newlsp() abort
|
||||
endif
|
||||
|
||||
" get the start of the rest
|
||||
let l:rest_start_idx = l:body_start_idx + str2nr(l:length_match[1])
|
||||
let l:next_start_idx = l:body_start_idx + str2nr(l:length_match[1])
|
||||
|
||||
if len(l:rest) < l:rest_start_idx
|
||||
if len(l:rest) < l:next_start_idx
|
||||
" incomplete response body
|
||||
break
|
||||
endif
|
||||
|
||||
if go#util#HasDebug('lsp')
|
||||
let g:go_lsp_log = add(go#config#LspLog(), "<-\n" . l:rest[:l:rest_start_idx - 1])
|
||||
endif
|
||||
call s:debug('received', l:rest[:l:next_start_idx - 1])
|
||||
|
||||
let l:body = l:rest[l:body_start_idx : l:rest_start_idx - 1]
|
||||
let l:rest = l:rest[l:rest_start_idx :]
|
||||
let l:body = l:rest[l:body_start_idx : l:next_start_idx - 1]
|
||||
let l:rest = l:rest[l:next_start_idx :]
|
||||
|
||||
try
|
||||
" add the json body to the list.
|
||||
@ -105,46 +112,99 @@ function! s:newlsp() abort
|
||||
function! l:lsp.handleMessage(ch, data) dict abort
|
||||
let self.buf .= a:data
|
||||
|
||||
let [self.buf, l:responses] = self.readMessage(self.buf)
|
||||
let [self.buf, l:messages] = self.readMessage(self.buf)
|
||||
|
||||
" TODO(bc): handle notifications (e.g. window/showMessage).
|
||||
|
||||
for l:response in l:responses
|
||||
if has_key(l:response, 'id') && has_key(self.handlers, l:response.id)
|
||||
try
|
||||
let l:handler = self.handlers[l:response.id]
|
||||
|
||||
let l:winid = win_getid(winnr())
|
||||
" Always set the active window to the window that was active when
|
||||
" the request was sent. 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
|
||||
" sending the reques.
|
||||
call win_gotoid(l:handler.winid)
|
||||
|
||||
if has_key(l:response, 'error')
|
||||
call l:handler.requestComplete(0)
|
||||
if has_key(l:handler, 'error')
|
||||
call call(l:handler.error, [l:response.error.message])
|
||||
else
|
||||
call go#util#EchoError(l:response.error.message)
|
||||
endif
|
||||
call win_gotoid(l:winid)
|
||||
return
|
||||
endif
|
||||
call l:handler.requestComplete(1)
|
||||
call call(l:handler.handleResult, [l:response.result])
|
||||
call win_gotoid(l:winid)
|
||||
finally
|
||||
call remove(self.handlers, l:response.id)
|
||||
endtry
|
||||
for l:message in l:messages
|
||||
if has_key(l:message, 'method')
|
||||
if has_key(l:message, 'id')
|
||||
call self.handleRequest(l:message)
|
||||
else
|
||||
call self.handleNotification(l:message)
|
||||
endif
|
||||
elseif has_key(l:message, 'result') || has_key(l:message, 'error')
|
||||
call self.handleResponse(l:message)
|
||||
endif
|
||||
endfor
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleRequest(req) dict abort
|
||||
if a:req.method == 'workspace/workspaceFolders'
|
||||
let l:resp = go#lsp#message#WorkspaceFoldersResult(self.workspaceDirectories)
|
||||
elseif a:req.method == 'workspace/configuration' && has_key(a:req, 'params') && has_key(a:req.params, 'items')
|
||||
let l:resp = go#lsp#message#ConfigurationResult(a:req.params.items)
|
||||
elseif a:req.method == 'client/registerCapability' && has_key(a:req, 'params') && has_key(a:req.params, 'registrations')
|
||||
let l:resp = v:null
|
||||
else
|
||||
return
|
||||
endif
|
||||
|
||||
if get(self, 'exited', 0)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:msg = self.newResponse(a:req.id, l:resp)
|
||||
call self.write(l:msg)
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleNotification(req) dict abort
|
||||
" TODO(bc): handle notifications (e.g. window/showMessage).
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleResponse(resp) dict abort
|
||||
if has_key(a:resp, 'id') && has_key(self.handlers, a:resp.id)
|
||||
try
|
||||
let l:handler = self.handlers[a:resp.id]
|
||||
|
||||
let l:winid = win_getid(winnr())
|
||||
" Always set the active window to the window that was active when
|
||||
" the request was sent. 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
|
||||
" sending the request.
|
||||
call win_gotoid(l:handler.winid)
|
||||
|
||||
if has_key(a:resp, 'error')
|
||||
call l:handler.requestComplete(0)
|
||||
if has_key(l:handler, 'error')
|
||||
call call(l:handler.error, [a:resp.error.message])
|
||||
else
|
||||
call go#util#EchoError(a:resp.error.message)
|
||||
endif
|
||||
call win_gotoid(l:winid)
|
||||
return
|
||||
endif
|
||||
call l:handler.requestComplete(1)
|
||||
|
||||
let l:winidBeforeHandler = l:handler.winid
|
||||
call call(l:handler.handleResult, [a:resp.result])
|
||||
|
||||
" change the window back to the window that was active when
|
||||
" starting to handle the message _only_ if the handler didn't
|
||||
" update the winid, so that handlers can set the winid if needed
|
||||
" (e.g. :GoDef).
|
||||
if l:handler.winid == l:winidBeforeHandler
|
||||
call win_gotoid(l:winid)
|
||||
endif
|
||||
finally
|
||||
call remove(self.handlers, a:resp.id)
|
||||
endtry
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! l:lsp.handleInitializeResult(result) dict abort
|
||||
if go#config#EchoCommandInfo()
|
||||
call go#util#EchoProgress("initialized gopls")
|
||||
endif
|
||||
let status = {
|
||||
\ 'desc': '',
|
||||
\ 'type': 'gopls',
|
||||
\ 'state': 'initialized',
|
||||
\ }
|
||||
call go#statusline#Update(self.wd, status)
|
||||
|
||||
let self.ready = 1
|
||||
" TODO(bc): send initialized message to the server?
|
||||
let l:msg = self.newMessage(go#lsp#message#Initialized())
|
||||
call self.write(l:msg)
|
||||
|
||||
" send messages queued while waiting for ready.
|
||||
for l:item in self.queue
|
||||
@ -157,22 +217,34 @@ function! s:newlsp() abort
|
||||
|
||||
function! l:lsp.sendMessage(data, handler) dict abort
|
||||
if !self.last_request_id
|
||||
" TODO(bc): run a server per module and one per GOPATH? (may need to
|
||||
" keep track of servers by rootUri).
|
||||
let l:wd = go#util#ModuleRoot()
|
||||
if l:wd == -1
|
||||
call go#util#EchoError('could not determine appropriate working directory for gopls')
|
||||
return
|
||||
return -1
|
||||
endif
|
||||
|
||||
if l:wd == ''
|
||||
let l:wd = getcwd()
|
||||
endif
|
||||
let self.wd = l:wd
|
||||
|
||||
if go#config#EchoCommandInfo()
|
||||
call go#util#EchoProgress("initializing gopls")
|
||||
endif
|
||||
|
||||
let l:status = {
|
||||
\ 'desc': '',
|
||||
\ 'type': 'gopls',
|
||||
\ 'state': 'initializing',
|
||||
\ }
|
||||
call go#statusline#Update(l:wd, l:status)
|
||||
|
||||
let self.workspaceDirectories = add(self.workspaceDirectories, l:wd)
|
||||
let l:msg = self.newMessage(go#lsp#message#Initialize(l:wd))
|
||||
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('self.handleInitializeResult', [], l:self)
|
||||
|
||||
let self.handlers[l:msg.id] = l:state
|
||||
|
||||
call l:state.start()
|
||||
@ -199,7 +271,7 @@ function! s:newlsp() abort
|
||||
let l:msg = {
|
||||
\ 'method': a:data.method,
|
||||
\ 'jsonrpc': '2.0',
|
||||
\ }
|
||||
\ }
|
||||
|
||||
if !a:data.notification
|
||||
let self.last_request_id += 1
|
||||
@ -213,13 +285,21 @@ function! s:newlsp() abort
|
||||
return l:msg
|
||||
endfunction
|
||||
|
||||
function! l:lsp.write(msg) dict abort
|
||||
let l:body = json_encode(a:msg)
|
||||
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
||||
function l:lsp.newResponse(id, result) dict abort
|
||||
let l:msg = {
|
||||
\ 'jsonrpc': '2.0',
|
||||
\ 'id': a:id,
|
||||
\ 'result': a:result,
|
||||
\ }
|
||||
|
||||
if go#util#HasDebug('lsp')
|
||||
let g:go_lsp_log = add(go#config#LspLog(), "->\n" . l:data)
|
||||
endif
|
||||
return l:msg
|
||||
endfunction
|
||||
|
||||
function! l:lsp.write(msg) dict abort
|
||||
let l:body = json_encode(a:msg)
|
||||
let l:data = 'Content-Length: ' . strlen(l:body) . "\r\n\r\n" . l:body
|
||||
|
||||
call s:debug('sent', l:data)
|
||||
|
||||
if has('nvim')
|
||||
call chansend(self.job, l:data)
|
||||
@ -229,19 +309,39 @@ function! s:newlsp() abort
|
||||
call ch_sendraw(self.job, l:data)
|
||||
endfunction
|
||||
|
||||
function! l:lsp.exit_cb(job, exit_status) dict abort
|
||||
function! l:lsp.exit_cb(job, exit_status) dict
|
||||
let self.exited = 1
|
||||
if !get(self, 'restarting', 0)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:queue = self.queue
|
||||
|
||||
let l:workspaces = self.workspaceDirectories
|
||||
|
||||
call s:lspfactory.reset()
|
||||
let l:lsp = s:lspfactory.get()
|
||||
|
||||
" restore workspaces
|
||||
call call('go#lsp#AddWorkspaceDirectory', l:workspaces)
|
||||
" * send DidOpen messages for all buffers that have b:did_lsp_open set
|
||||
" TODO(bc): check modifiable and filetype, too?
|
||||
bufdo if get(b:, 'go_lsp_did_open', 0) | if &modified | call go#lsp#DidOpen(expand('%:p')) | else | call go#lsp#DidChange(expand('%:p')) | endif | endif
|
||||
let l:lsp.queue = extend(l:lsp.queue, l:queue)
|
||||
return
|
||||
endfunction
|
||||
" explicitly bind close_cb to state so that within it, self will always refer
|
||||
|
||||
function! l:lsp.close_cb(ch) dict abort
|
||||
" TODO(bc): does anything need to be done here?
|
||||
" TODO(bc): remove the buffer variables that indicate that gopls has been
|
||||
" informed that the file is open
|
||||
endfunction
|
||||
|
||||
function! l:lsp.err_cb(ch, msg) dict abort
|
||||
if go#util#HasDebug('lsp')
|
||||
let g:go_lsp_log = add(go#config#LspLog(), "<-stderr\n" . a:msg)
|
||||
if a:msg =~ '^\tPort = \d\+$' && !get(self, 'debugport', 0)
|
||||
let self.debugport = substitute(a:msg, '^\tPort = \(\d\+\).*$', '\1', '')
|
||||
endif
|
||||
|
||||
call s:debug('stderr', a:msg)
|
||||
endfunction
|
||||
|
||||
" explicitly bind callbacks to l:lsp so that within it, self will always refer
|
||||
@ -263,11 +363,13 @@ function! s:newlsp() abort
|
||||
return
|
||||
endif
|
||||
|
||||
" TODO(bc): output a message indicating which directory lsp is going to
|
||||
" start in.
|
||||
let l:lsp.job = go#job#Start([l:bin_path], l:opts)
|
||||
let l:cmd = [l:bin_path]
|
||||
if go#util#HasDebug('lsp')
|
||||
let l:cmd = extend(l:cmd, ['-debug', 'localhost:0'])
|
||||
endif
|
||||
|
||||
let l:lsp.job = go#job#Start(l:cmd, l:opts)
|
||||
|
||||
" TODO(bc): send the initialize message now?
|
||||
return l:lsp
|
||||
endfunction
|
||||
|
||||
@ -327,16 +429,17 @@ function! s:requestComplete(ok) abort dict
|
||||
endfunction
|
||||
|
||||
function! s:start() abort dict
|
||||
if self.statustype != ''
|
||||
let status = {
|
||||
\ 'desc': 'current status',
|
||||
\ 'type': self.statustype,
|
||||
\ 'state': "started",
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(self.jobdir, status)
|
||||
endif
|
||||
let self.started_at = reltime()
|
||||
if self.statustype == ''
|
||||
return
|
||||
endif
|
||||
let status = {
|
||||
\ 'desc': 'current status',
|
||||
\ 'type': self.statustype,
|
||||
\ 'state': "started",
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(self.jobdir, status)
|
||||
endfunction
|
||||
|
||||
" go#lsp#Definition calls gopls to get the definition of the identifier at
|
||||
@ -351,13 +454,13 @@ function! go#lsp#Definition(fname, line, col, handler) abort
|
||||
let l:state = s:newHandlerState('definition')
|
||||
let l:state.handleResult = funcref('s:definitionHandler', [function(a:handler, [], l:state)], l:state)
|
||||
let l:msg = go#lsp#message#Definition(fnamemodify(a:fname, ':p'), a:line, a:col)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:definitionHandler(next, msg) abort dict
|
||||
" gopls returns a []Location; just take the first one.
|
||||
let l:msg = a:msg[0]
|
||||
let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]]
|
||||
let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, go#lsp#lsp#PositionOf(getline(l:msg.range.start.line+1), l:msg.range.start.character), 'lsp does not supply a description')]]
|
||||
call call(a:next, l:args)
|
||||
endfunction
|
||||
|
||||
@ -373,13 +476,13 @@ function! go#lsp#TypeDef(fname, line, col, handler) abort
|
||||
let l:state = s:newHandlerState('type definition')
|
||||
let l:msg = go#lsp#message#TypeDefinition(fnamemodify(a:fname, ':p'), a:line, a:col)
|
||||
let l:state.handleResult = funcref('s:typeDefinitionHandler', [function(a:handler, [], l:state)], l:state)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:typeDefinitionHandler(next, msg) abort dict
|
||||
" gopls returns a []Location; just take the first one.
|
||||
let l:msg = a:msg[0]
|
||||
let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, l:msg.range.start.character+1, 'lsp does not supply a description')]]
|
||||
let l:args = [[printf('%s:%d:%d: %s', go#path#FromURI(l:msg.uri), l:msg.range.start.line+1, go#lsp#lsp#PositionOf(getline(l:msg.range.start.line+1), l:msg.range.start.character), 'lsp does not supply a description')]]
|
||||
call call(a:next, l:args)
|
||||
endfunction
|
||||
|
||||
@ -396,9 +499,13 @@ function! go#lsp#DidOpen(fname) abort
|
||||
let l:msg = go#lsp#message#DidOpen(fnamemodify(a:fname, ':p'), join(go#util#GetLines(), "\n") . "\n")
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
" TODO(bc): setting a buffer level variable here assumes that a:fname is the
|
||||
" current buffer. Change to a:fname first before setting it and then change
|
||||
" back to active buffer.
|
||||
let b:go_lsp_did_open = 1
|
||||
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#DidChange(fname) abort
|
||||
@ -409,17 +516,17 @@ function! go#lsp#DidChange(fname) abort
|
||||
return
|
||||
endif
|
||||
|
||||
call go#lsp#DidOpen(a:fname)
|
||||
|
||||
if !filereadable(a:fname)
|
||||
return
|
||||
endif
|
||||
|
||||
call go#lsp#DidOpen(a:fname)
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
let l:msg = go#lsp#message#DidChange(fnamemodify(a:fname, ':p'), join(go#util#GetLines(), "\n") . "\n")
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#DidClose(fname) abort
|
||||
@ -435,9 +542,12 @@ function! go#lsp#DidClose(fname) abort
|
||||
let l:msg = go#lsp#message#DidClose(fnamemodify(a:fname, ':p'))
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
" TODO(bc): setting a buffer level variable here assumes that a:fname is the
|
||||
" current buffer. Change to a:fname first before setting it and then change
|
||||
" back to active buffer.
|
||||
let b:go_lsp_did_open = 0
|
||||
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#Completion(fname, line, col, handler) abort
|
||||
@ -448,16 +558,33 @@ function! go#lsp#Completion(fname, line, col, handler) abort
|
||||
let l:state = s:newHandlerState('completion')
|
||||
let l:state.handleResult = funcref('s:completionHandler', [function(a:handler, [], l:state)], l:state)
|
||||
let l:state.error = funcref('s:completionErrorHandler', [function(a:handler, [], l:state)], l:state)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:completionHandler(next, msg) abort dict
|
||||
" gopls returns a CompletionList.
|
||||
let l:matches = []
|
||||
let l:start = -1
|
||||
|
||||
for l:item in a:msg.items
|
||||
let l:start = l:item.textEdit.range.start.character
|
||||
|
||||
let l:match = {'abbr': l:item.label, 'word': l:item.textEdit.newText, 'info': '', 'kind': go#lsp#completionitemkind#Vim(l:item.kind)}
|
||||
if has_key(l:item, 'detail')
|
||||
let l:match.info = l:item.detail
|
||||
let l:match.menu = l:item.detail
|
||||
if go#lsp#completionitemkind#IsFunction(l:item.kind) || go#lsp#completionitemkind#IsMethod(l:item.kind)
|
||||
let l:match.info = printf('%s %s', l:item.label, l:item.detail)
|
||||
|
||||
" The detail provided by gopls hasn't always provided the the full
|
||||
" signature including the return value. The label used to be the
|
||||
" function signature and the detail was the return value. Handle
|
||||
" that case for backward compatibility. This can be removed in the
|
||||
" future once it's likely that the majority of users are on a recent
|
||||
" version of gopls.
|
||||
if l:item.detail !~ '^func'
|
||||
let l:match.info = printf('func %s %s', l:item.label, l:item.detail)
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(l:item, 'documentation')
|
||||
@ -466,12 +593,12 @@ function! s:completionHandler(next, msg) abort dict
|
||||
|
||||
let l:matches = add(l:matches, l:match)
|
||||
endfor
|
||||
let l:args = [l:matches]
|
||||
let l:args = [l:start, l:matches]
|
||||
call call(a:next, l:args)
|
||||
endfunction
|
||||
|
||||
function! s:completionErrorHandler(next, error) abort dict
|
||||
call call(a:next, [[]])
|
||||
call call(a:next, [-1, []])
|
||||
endfunction
|
||||
|
||||
function! go#lsp#Hover(fname, line, col, handler) abort
|
||||
@ -482,7 +609,7 @@ function! go#lsp#Hover(fname, line, col, handler) abort
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:hoverHandler', [function(a:handler, [], l:state)], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:hoverHandler(next, msg) abort dict
|
||||
@ -499,7 +626,7 @@ endfunction
|
||||
|
||||
function! go#lsp#Info(showstatus)
|
||||
let l:fname = expand('%:p')
|
||||
let [l:line, l:col] = getpos('.')[1:2]
|
||||
let [l:line, l:col] = go#lsp#lsp#Position()
|
||||
|
||||
call go#lsp#DidChange(l:fname)
|
||||
|
||||
@ -511,10 +638,29 @@ function! go#lsp#Info(showstatus)
|
||||
let l:state = s:newHandlerState('')
|
||||
endif
|
||||
|
||||
let l:state.handleResult = funcref('s:infoDefinitionHandler', [function('s:info', []), a:showstatus], l:state)
|
||||
let l:state.handleResult = funcref('s:infoDefinitionHandler', [function('s:info', [1], l:state), a:showstatus], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
let l:msg = go#lsp#message#Definition(l:fname, l:line, l:col)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! go#lsp#GetInfo()
|
||||
let l:fname = expand('%:p')
|
||||
let [l:line, l:col] = go#lsp#lsp#Position()
|
||||
|
||||
call go#lsp#DidChange(l:fname)
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
|
||||
let l:state = s:newHandlerState('')
|
||||
|
||||
let l:info = go#promise#New(function('s:info', [0], l:state), 10000, '')
|
||||
|
||||
let l:state.handleResult = funcref('s:infoDefinitionHandler', [l:info.wrapper, 0], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
let l:msg = go#lsp#message#Definition(l:fname, l:line, l:col)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:info.await()
|
||||
endfunction
|
||||
|
||||
function! s:infoDefinitionHandler(next, showstatus, msg) abort dict
|
||||
@ -522,8 +668,8 @@ function! s:infoDefinitionHandler(next, showstatus, msg) abort dict
|
||||
let l:msg = a:msg[0]
|
||||
|
||||
let l:fname = go#path#FromURI(l:msg.uri)
|
||||
let l:line = l:msg.range.start.line+1
|
||||
let l:col = l:msg.range.start.character+1
|
||||
let l:line = l:msg.range.start.line
|
||||
let l:col = l:msg.range.start.character
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
let l:msg = go#lsp#message#Hover(l:fname, l:line, l:col)
|
||||
@ -534,18 +680,166 @@ function! s:infoDefinitionHandler(next, showstatus, msg) abort dict
|
||||
let l:state = s:newHandlerState('')
|
||||
endif
|
||||
|
||||
let l:state.handleResult = funcref('s:hoverHandler', [function('s:info', [], l:state)], l:state)
|
||||
let l:state.handleResult = funcref('s:hoverHandler', [a:next], l:state)
|
||||
let l:state.error = funcref('s:noop')
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
return l:lsp.sendMessage(l:msg, l:state)
|
||||
endfunction
|
||||
|
||||
function! s:info(content) abort dict
|
||||
function! s:info(show, content) abort dict
|
||||
let l:content = s:infoFromHoverContent(a:content)
|
||||
|
||||
if a:show
|
||||
call go#util#ShowInfo(l:content)
|
||||
endif
|
||||
|
||||
return l:content
|
||||
endfunction
|
||||
|
||||
function! s:infoFromHoverContent(content) abort
|
||||
if len(a:content) < 1
|
||||
return ''
|
||||
endif
|
||||
|
||||
let l:content = a:content[0]
|
||||
|
||||
" strip off the method set and fields of structs and interfaces.
|
||||
if l:content =~# '^type [^ ]\+ \(struct\|interface\)'
|
||||
if l:content =~# '^\(type \)\?[^ ]\+ \(struct\|interface\)'
|
||||
let l:content = substitute(l:content, '{.*', '', '')
|
||||
endif
|
||||
call go#util#ShowInfo(l:content)
|
||||
|
||||
return l:content
|
||||
endfunction
|
||||
|
||||
function! go#lsp#AddWorkspaceDirectory(...) abort
|
||||
if a:0 == 0
|
||||
return
|
||||
endif
|
||||
|
||||
call go#lsp#CleanWorkspaces()
|
||||
|
||||
let l:workspaces = []
|
||||
for l:dir in a:000
|
||||
let l:dir = fnamemodify(l:dir, ':p')
|
||||
if !isdirectory(l:dir)
|
||||
continue
|
||||
endif
|
||||
|
||||
let l:workspaces = add(l:workspaces, l:dir)
|
||||
endfor
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
let l:lsp.workspaceDirectories = extend(l:lsp.workspaceDirectories, l:workspaces)
|
||||
let l:msg = go#lsp#message#ChangeWorkspaceFolders(l:workspaces, [])
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! go#lsp#CleanWorkspaces() abort
|
||||
let l:workspaces = []
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
|
||||
let l:i = 0
|
||||
let l:missing = []
|
||||
for l:dir in l:lsp.workspaceDirectories
|
||||
if !isdirectory(l:dir)
|
||||
let l:dir = add(l:missing, l:dir)
|
||||
call remove(l:lsp.workspaceDirectories, l:i)
|
||||
continue
|
||||
endif
|
||||
let l:i += 1
|
||||
endfor
|
||||
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
let l:msg = go#lsp#message#ChangeWorkspaceFolders([], l:missing)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" go#lsp#ResetWorkspaceDiretories removes and then re-adds all workspace
|
||||
" folders to cause gopls to send configuration requests for all of them again.
|
||||
" This is useful, for instance, when build tags have been added and gopls
|
||||
" needs to use them.
|
||||
function! go#lsp#ResetWorkspaceDirectories() abort
|
||||
call go#lsp#CleanWorkspaces()
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
|
||||
let l:state = s:newHandlerState('')
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
let l:msg = go#lsp#message#ChangeWorkspaceFolders(l:lsp.workspaceDirectories, l:lsp.workspaceDirectories)
|
||||
call l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! go#lsp#DebugBrowser() abort
|
||||
let l:lsp = s:lspfactory.get()
|
||||
let l:port = get(l:lsp, 'debugport', 0)
|
||||
if !l:port
|
||||
call go#util#EchoError("gopls was not started with debugging enabled. See :help g:go_debug.")
|
||||
return
|
||||
endif
|
||||
|
||||
call go#util#OpenBrowser(printf('http://localhost:%d', l:port))
|
||||
endfunction
|
||||
|
||||
function! go#lsp#Restart() abort
|
||||
if !go#util#has_job() || len(s:lspfactory) == 0 || !has_key(s:lspfactory, 'current')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:lsp = s:lspfactory.get()
|
||||
|
||||
let l:lsp.restarting = 1
|
||||
|
||||
let l:state = s:newHandlerState('exit')
|
||||
|
||||
let l:msg = go#lsp#message#Shutdown()
|
||||
let l:state.handleResult = funcref('s:noop')
|
||||
let l:retval = l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
let l:msg = go#lsp#message#Exit()
|
||||
let l:retval = l:lsp.sendMessage(l:msg, l:state)
|
||||
|
||||
return l:retval
|
||||
endfunction
|
||||
|
||||
function! s:debug(event, data) abort
|
||||
if !go#util#HasDebug('lsp')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:winid = win_getid()
|
||||
|
||||
let l:name = '__GOLSP_LOG__'
|
||||
let l:log_winid = bufwinid(l:name)
|
||||
if l:log_winid == -1
|
||||
silent keepalt botright 10new
|
||||
silent file `='__GOLSP_LOG__'`
|
||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||
setlocal filetype=golsplog
|
||||
else
|
||||
call win_gotoid(l:log_winid)
|
||||
endif
|
||||
|
||||
try
|
||||
setlocal modifiable
|
||||
if getline(1) == ''
|
||||
call setline('$', printf('%s: %s', a:event, a:data))
|
||||
else
|
||||
call append('$', printf('%s: %s', a:event, a:data))
|
||||
endif
|
||||
normal! G
|
||||
setlocal nomodifiable
|
||||
finally
|
||||
call win_gotoid(l:winid)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
|
@ -28,7 +28,7 @@ let s:Event = 23
|
||||
let s:Operator = 24
|
||||
let s:TypeParameter = 25
|
||||
|
||||
function! go#lsp#completionitemkind#Vim(kind)
|
||||
function! go#lsp#completionitemkind#Vim(kind) abort
|
||||
if a:kind == s:Method || a:kind == s:Function || a:kind == s:Constructor
|
||||
return 'f'
|
||||
elseif a:kind == s:Variable || a:kind == s:Constant
|
||||
@ -40,6 +40,22 @@ function! go#lsp#completionitemkind#Vim(kind)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! go#lsp#completionitemkind#IsFunction(kind) abort
|
||||
if a:kind == s:Function
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
function! go#lsp#completionitemkind#IsMethod(kind) abort
|
||||
if a:kind == s:Method
|
||||
return 1
|
||||
endif
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
58
sources_non_forked/vim-go/autoload/go/lsp/lsp.vim
Normal file
58
sources_non_forked/vim-go/autoload/go/lsp/lsp.vim
Normal file
@ -0,0 +1,58 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
" go#lsp#lsp#Position returns the LSP text position. If no arguments are
|
||||
" provided, the cursor position is assumed. Otherwise, there should be two
|
||||
" arguments: the line and the column.
|
||||
function! go#lsp#lsp#Position(...)
|
||||
if a:0 < 2
|
||||
let [l:line, l:col] = getpos('.')[1:2]
|
||||
else
|
||||
let l:line = a:1
|
||||
let l:col = a:2
|
||||
endif
|
||||
let l:content = getline(l:line)
|
||||
|
||||
" LSP uses 0-based lines.
|
||||
return [l:line - 1, s:character(l:line, l:col-1)]
|
||||
endfunction
|
||||
|
||||
function! s:strlen(str) abort
|
||||
let l:runes = split(a:str, '\zs')
|
||||
return len(l:runes) + len(filter(l:runes, 'char2nr(v:val)>=0x10000'))
|
||||
endfunction
|
||||
|
||||
function! s:character(line, col) abort
|
||||
return s:strlen(getline(a:line)[:col([a:line, a:col - 1])])
|
||||
endfunction
|
||||
|
||||
" go#lsp#PositionOf returns len(content[0:units]) where units is utf-16 code
|
||||
" units. This is mostly useful for converting LSP text position to vim
|
||||
" position.
|
||||
function! go#lsp#lsp#PositionOf(content, units) abort
|
||||
if a:units == 0
|
||||
return 1
|
||||
endif
|
||||
|
||||
let l:remaining = a:units
|
||||
let l:str = ""
|
||||
for l:rune in split(a:content, '\zs')
|
||||
if l:remaining < 0
|
||||
break
|
||||
endif
|
||||
let l:remaining -= 1
|
||||
if char2nr(l:rune) >= 0x10000
|
||||
let l:remaining -= 1
|
||||
endif
|
||||
let l:str = l:str . l:rune
|
||||
endfor
|
||||
|
||||
return len(l:str)
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
32
sources_non_forked/vim-go/autoload/go/lsp/lsp_test.vim
Normal file
32
sources_non_forked/vim-go/autoload/go/lsp/lsp_test.vim
Normal file
@ -0,0 +1,32 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
function! Test_PositionOf_Simple()
|
||||
let l:actual = go#lsp#lsp#PositionOf("just ascii", 3)
|
||||
call assert_equal(4, l:actual)
|
||||
endfunc
|
||||
|
||||
|
||||
function! Test_PositionOf_MultiByte()
|
||||
" ⌘ is U+2318, which encodes to three bytes in utf-8 and 1 code unit in
|
||||
" utf-16.
|
||||
let l:actual = go#lsp#lsp#PositionOf("⌘⌘ foo", 3)
|
||||
call assert_equal(8, l:actual)
|
||||
endfunc
|
||||
|
||||
function! Test_PositionOf_MultipleCodeUnit()
|
||||
" 𐐀 is U+10400, which encodes to 4 bytes in utf-8 and 2 code units in
|
||||
" utf-16.
|
||||
let l:actual = go#lsp#lsp#PositionOf("𐐀 bar", 3)
|
||||
call assert_equal(6, l:actual)
|
||||
endfunction
|
||||
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
@ -10,17 +10,50 @@ function! go#lsp#message#Initialize(wd) abort
|
||||
\ 'processId': getpid(),
|
||||
\ 'rootUri': go#path#ToURI(a:wd),
|
||||
\ 'capabilities': {
|
||||
\ 'workspace': {},
|
||||
\ 'workspace': {
|
||||
\ 'workspaceFolders': v:true,
|
||||
\ 'didChangeConfiguration': {
|
||||
\ 'dynamicRegistration': v:true,
|
||||
\ },
|
||||
\ 'configuration': v:true,
|
||||
\ },
|
||||
\ 'textDocument': {
|
||||
\ 'hover': {
|
||||
\ 'contentFormat': ['plaintext'],
|
||||
\ },
|
||||
\ }
|
||||
\ }
|
||||
\ },
|
||||
\ 'workspaceFolders': [s:workspaceFolder(0, a:wd)],
|
||||
\ }
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Initialized() abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'initialized',
|
||||
\ 'params': {},
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Shutdown() abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
\ 'method': 'shutdown',
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Exit() abort
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'exit',
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#WorkspaceFoldersResult(dirs) abort
|
||||
return map(copy(a:dirs), function('s:workspaceFolder', []))
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#Definition(file, line, col) abort
|
||||
return {
|
||||
\ 'notification': 0,
|
||||
@ -116,8 +149,49 @@ function! go#lsp#message#Hover(file, line, col) abort
|
||||
\ }
|
||||
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', []))
|
||||
|
||||
return {
|
||||
\ 'notification': 1,
|
||||
\ 'method': 'workspace/didChangeWorkspaceFolders',
|
||||
\ 'params': {
|
||||
\ 'event': {
|
||||
\ 'removed': l:removeDirs,
|
||||
\ 'added': l:addDirs,
|
||||
\ },
|
||||
\ }
|
||||
\ }
|
||||
|
||||
endfunction
|
||||
|
||||
function! go#lsp#message#ConfigurationResult(items) abort
|
||||
let l:result = []
|
||||
|
||||
" results must be in the same order as the items
|
||||
for l:item in a:items
|
||||
let l:config = {
|
||||
\ 'buildFlags': [],
|
||||
\ 'hoverKind': 'NoDocumentation',
|
||||
\ }
|
||||
let l:buildtags = go#config#BuildTags()
|
||||
if buildtags isnot ''
|
||||
let l:config.buildFlags = extend(l:config.buildFlags, ['-tags', go#config#BuildTags()])
|
||||
endif
|
||||
|
||||
let l:result = add(l:result, l:config)
|
||||
endfor
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function s:workspaceFolder(key, val) abort
|
||||
return {'uri': go#path#ToURI(a:val), 'name': a:val}
|
||||
endfunction
|
||||
|
||||
function! s:position(line, col) abort
|
||||
return {'line': a:line - 1, 'character': a:col-1}
|
||||
return {'line': a:line, 'character': a:col}
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
|
49
sources_non_forked/vim-go/autoload/go/lsp_test.vim
Normal file
49
sources_non_forked/vim-go/autoload/go/lsp_test.vim
Normal file
@ -0,0 +1,49 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
function! Test_GetSimpleTextPosition()
|
||||
call s:getinfo('lsp text position should align with cursor position after ascii only', 'ascii')
|
||||
endfunction
|
||||
|
||||
function! Test_GetMultiByteTextPosition()
|
||||
call s:getinfo('lsp text position should align with cursor position after two place of interest symbols ⌘⌘', 'multi-byte')
|
||||
endfunction
|
||||
|
||||
function! Test_GetMultipleCodeUnitTextPosition()
|
||||
call s:getinfo('lsp text position should align with cursor position after Deseret Capital Letter Long I 𐐀', 'multi-code-units')
|
||||
endfunction
|
||||
|
||||
function! s:getinfo(str, name)
|
||||
if !go#util#has_job()
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let g:go_info_mode = 'gopls'
|
||||
|
||||
let l:tmp = gotest#write_file(a:name . '/position/position.go', [
|
||||
\ 'package position',
|
||||
\ '',
|
||||
\ 'func Example() {',
|
||||
\ "\tid := " . '"foo"',
|
||||
\ "\tprintln(" .'"' . a:str . '", id)',
|
||||
\ '}',
|
||||
\ ] )
|
||||
|
||||
let l:expected = 'var id string'
|
||||
let l:actual = go#lsp#GetInfo()
|
||||
call assert_equal(l:expected, l:actual)
|
||||
finally
|
||||
"call delete(l:tmp, 'rf')
|
||||
unlet g:go_info_mode
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
@ -8,10 +8,14 @@ function! go#mod#Format() abort
|
||||
" go mod only exists in `v1.11`
|
||||
if empty(s:go_major_version)
|
||||
let tokens = matchlist(go#util#Exec(['go', 'version']), '\d\+.\(\d\+\)\(\.\d\+\)\? ')
|
||||
let s:go_major_version = str2nr(tokens[1])
|
||||
if len(tokens) > 0
|
||||
let s:go_major_version = str2nr(tokens[1])
|
||||
else
|
||||
let s:go_major_version = ""
|
||||
endif
|
||||
endif
|
||||
|
||||
if s:go_major_version < "11"
|
||||
if !empty(s:go_major_version) && s:go_major_version < "11"
|
||||
call go#util#EchoError("Go v1.11 is required to format go.mod file")
|
||||
return
|
||||
endif
|
||||
|
@ -82,6 +82,10 @@ function! s:vendordirs() abort
|
||||
if l:err != 0
|
||||
return []
|
||||
endif
|
||||
if empty(l:root)
|
||||
return []
|
||||
endif
|
||||
|
||||
let l:root = split(l:root, '\n')[0] . go#util#PathSep() . 'src'
|
||||
|
||||
let [l:dir, l:err] = go#util#ExecInDir(['go', 'list', '-f', '{{.Dir}}'])
|
||||
@ -111,57 +115,77 @@ function! s:vendordirs() abort
|
||||
endfunction
|
||||
|
||||
let s:import_paths = {}
|
||||
" ImportPath returns the import path of the package for current buffer.
|
||||
" ImportPath returns the import path of the package for current buffer. It
|
||||
" returns -1 if the import path cannot be determined.
|
||||
function! go#package#ImportPath() abort
|
||||
let dir = expand("%:p:h")
|
||||
let l:dir = expand("%:p:h")
|
||||
if has_key(s:import_paths, dir)
|
||||
return s:import_paths[dir]
|
||||
return s:import_paths[l:dir]
|
||||
endif
|
||||
|
||||
let [l:out, l:err] = go#util#ExecInDir(['go', 'list'])
|
||||
if l:err != 0
|
||||
let l:importpath = go#package#FromPath(l:dir)
|
||||
if type(l:importpath) == type(0)
|
||||
return -1
|
||||
endif
|
||||
|
||||
let l:importpath = split(out, '\n')[0]
|
||||
|
||||
" go list returns '_CURRENTDIRECTORY' if the directory is not inside GOPATH.
|
||||
" Check it and retun an error if that is the case
|
||||
if l:importpath[0] ==# '_'
|
||||
return -1
|
||||
endif
|
||||
|
||||
let s:import_paths[dir] = l:importpath
|
||||
let s:import_paths[l:dir] = l:importpath
|
||||
|
||||
return l:importpath
|
||||
endfunction
|
||||
|
||||
|
||||
" go#package#FromPath returns the import path of arg. -1 is returned when arg
|
||||
" does not specify a package. -2 is returned when arg is a relative path
|
||||
" outside of GOPATH and not in a module.
|
||||
" outside of GOPATH, not in a module, and not below the current working
|
||||
" directory. A relative path is returned when in a null module at or below the
|
||||
" current working directory..
|
||||
function! go#package#FromPath(arg) abort
|
||||
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd'
|
||||
let l:dir = getcwd()
|
||||
|
||||
let l:path = a:arg
|
||||
let l:path = fnamemodify(a:arg, ':p')
|
||||
if !isdirectory(l:path)
|
||||
let l:path = fnamemodify(l:path, ':h')
|
||||
endif
|
||||
|
||||
execute l:cd fnameescape(l:path)
|
||||
let [l:out, l:err] = go#util#Exec(['go', 'list'])
|
||||
execute l:cd fnameescape(l:dir)
|
||||
if l:err != 0
|
||||
return -1
|
||||
endif
|
||||
try
|
||||
if glob("*.go") == ""
|
||||
" There's no Go code in this directory. We might be in a module directory
|
||||
" which doesn't have any code at this level. To avoid `go list` making a
|
||||
" bunch of HTTP requests to fetch dependencies, short-circuit `go list`
|
||||
" and return -1 immediately.
|
||||
if !empty(s:module())
|
||||
return -1
|
||||
endif
|
||||
endif
|
||||
let [l:out, l:err] = go#util#Exec(['go', 'list'])
|
||||
if l:err != 0
|
||||
return -1
|
||||
endif
|
||||
|
||||
let l:importpath = split(l:out, '\n')[0]
|
||||
let l:importpath = split(l:out, '\n')[0]
|
||||
finally
|
||||
execute l:cd fnameescape(l:dir)
|
||||
endtry
|
||||
|
||||
" go list returns '_CURRENTDIRECTORY' if the directory is neither in GOPATH
|
||||
" nor in a module. Check it and retun an error if that is the case
|
||||
" go list returns '_CURRENTDIRECTORY' if the directory is in a null module
|
||||
" (i.e. neither in GOPATH nor in a module). Return a relative import path
|
||||
" if possible or an error if that is the case.
|
||||
if l:importpath[0] ==# '_'
|
||||
return -2
|
||||
let l:relativeimportpath = fnamemodify(l:importpath[1:], ':.')
|
||||
if go#util#IsWin()
|
||||
let l:relativeimportpath = substitute(l:relativeimportpath, '\\', '/', 'g')
|
||||
endif
|
||||
|
||||
if l:relativeimportpath == l:importpath[1:]
|
||||
return '.'
|
||||
endif
|
||||
|
||||
if l:relativeimportpath[0] == '/'
|
||||
return -2
|
||||
endif
|
||||
|
||||
let l:importpath= printf('./%s', l:relativeimportpath)
|
||||
endif
|
||||
|
||||
return l:importpath
|
||||
|
50
sources_non_forked/vim-go/autoload/go/promise.vim
Normal file
50
sources_non_forked/vim-go/autoload/go/promise.vim
Normal file
@ -0,0 +1,50 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
scriptencoding utf-8
|
||||
|
||||
" New returns a promise. A promise's primary purpose is to make async jobs
|
||||
" synchronous by awaiting fn.
|
||||
"
|
||||
" A promise is a dictionary with two keys:
|
||||
" 'wrapper':
|
||||
" A function that wraps fn. It can be used in place of fn.
|
||||
" 'await':
|
||||
" A function that waits for wrapper to be called and returns the value
|
||||
" returned by fn. Returns default if timeout expires.
|
||||
function! go#promise#New(fn, timeout, default) abort
|
||||
let l:state = {}
|
||||
|
||||
" explicitly bind to state so that within l:promise's methods, self will
|
||||
" always refer to state. See :help Partial for more information.
|
||||
return {
|
||||
\ 'wrapper': function('s:wrapper', [a:fn], l:state),
|
||||
\ 'await': function('s:await', [a:timeout, a:default], l:state),
|
||||
\ }
|
||||
endfunction
|
||||
|
||||
function! s:wrapper(fn, ...) dict
|
||||
let self.retval = call(a:fn, a:000)
|
||||
return self.retval
|
||||
endfunction
|
||||
|
||||
function! s:await(timeout, default) dict
|
||||
let l:timer = timer_start(a:timeout, function('s:setretval', [a:default], self))
|
||||
while !has_key(self, 'retval')
|
||||
sleep 50m
|
||||
endwhile
|
||||
call timer_stop(l:timer)
|
||||
|
||||
return self.retval
|
||||
endfunction
|
||||
|
||||
function! s:setretval(val, timer) dict
|
||||
let self.retval = a:val
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
41
sources_non_forked/vim-go/autoload/go/promise_test.vim
Normal file
41
sources_non_forked/vim-go/autoload/go/promise_test.vim
Normal file
@ -0,0 +1,41 @@
|
||||
" don't spam the user when Vim is started in Vi compatibility mode
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
func! Test_PromiseNew() abort
|
||||
let l:sut = go#promise#New(function('s:work', []), 100, -1)
|
||||
call assert_true(has_key(l:sut, 'wrapper'))
|
||||
call assert_true(has_key(l:sut, 'await'))
|
||||
endfunc
|
||||
|
||||
func! Test_PromiseAwait() abort
|
||||
let l:expected = 1
|
||||
let l:default = -1
|
||||
let l:sut = go#promise#New(function('s:work', [l:expected]), 100, l:default)
|
||||
|
||||
call timer_start(10, l:sut.wrapper)
|
||||
|
||||
let l:actual = call(l:sut.await, [])
|
||||
call assert_equal(l:expected, l:actual)
|
||||
endfunc
|
||||
|
||||
func! Test_PromiseAwait_Timeout() abort
|
||||
let l:desired = 1
|
||||
let l:expected = -1
|
||||
let l:sut = go#promise#New(function('s:work', [l:desired]), 10, l:expected)
|
||||
|
||||
call timer_start(100, l:sut.wrapper)
|
||||
|
||||
let l:actual = call(l:sut.await, [])
|
||||
call assert_equal(l:expected, l:actual)
|
||||
endfunc
|
||||
|
||||
func! s:work(val, timer)
|
||||
return a:val
|
||||
endfunc
|
||||
|
||||
" restore Vi compatibility settings
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: sw=2 ts=2 et
|
@ -27,9 +27,8 @@ let s:last_status = ""
|
||||
" if not it returns an empty string. This function should be plugged directly
|
||||
" into the statusline.
|
||||
function! go#statusline#Show() abort
|
||||
" lazy initialiation of the cleaner
|
||||
" lazy initialization of the cleaner
|
||||
if !s:timer_id
|
||||
" clean every 60 seconds all statuses
|
||||
let interval = go#config#StatuslineDuration()
|
||||
let s:timer_id = timer_start(interval, function('go#statusline#Clear'), {'repeat': -1})
|
||||
endif
|
||||
@ -57,9 +56,9 @@ function! go#statusline#Show() abort
|
||||
|
||||
" only update highlight if status has changed.
|
||||
if status_text != s:last_status
|
||||
if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass"
|
||||
if status.state =~ "success" || status.state =~ "finished" || status.state =~ "pass" || status.state =~ 'initialized'
|
||||
hi goStatusLineColor cterm=bold ctermbg=76 ctermfg=22 guibg=#5fd700 guifg=#005f00
|
||||
elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling"
|
||||
elseif status.state =~ "started" || status.state =~ "analysing" || status.state =~ "compiling" || status.state =~ 'initializing'
|
||||
hi goStatusLineColor cterm=bold ctermbg=208 ctermfg=88 guibg=#ff8700 guifg=#870000
|
||||
elseif status.state =~ "failed"
|
||||
hi goStatusLineColor cterm=bold ctermbg=196 ctermfg=52 guibg=#ff0000 guifg=#5f0000
|
||||
@ -83,10 +82,11 @@ function! go#statusline#Update(status_dir, status) abort
|
||||
" before we stop the timer, check if we have any previous jobs to be cleaned
|
||||
" up. Otherwise every job will reset the timer when this function is called
|
||||
" and thus old jobs will never be cleaned
|
||||
call go#statusline#Clear(0)
|
||||
call s:clear()
|
||||
|
||||
" also reset the timer, so the user has time to see it in the statusline.
|
||||
" Setting the timer_id to 0 will trigger a new cleaner routine.
|
||||
" Setting the timer_id to 0 will cause a new timer to be created the next
|
||||
" time the go#statusline#Show() is called.
|
||||
call timer_stop(s:timer_id)
|
||||
let s:timer_id = 0
|
||||
endfunction
|
||||
@ -94,6 +94,10 @@ endfunction
|
||||
" Clear clears all currently stored statusline data. The timer_id argument is
|
||||
" just a placeholder so we can pass it to a timer_start() function if needed.
|
||||
function! go#statusline#Clear(timer_id) abort
|
||||
call s:clear()
|
||||
endfunction
|
||||
|
||||
function! s:clear()
|
||||
for [status_dir, status] in items(s:statuses)
|
||||
let elapsed_time = reltimestr(reltime(status.created_at))
|
||||
" strip whitespace
|
||||
|
@ -24,8 +24,13 @@ function! go#template#create() abort
|
||||
else
|
||||
let l:template_file = go#config#TemplateFile()
|
||||
endif
|
||||
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
|
||||
silent exe 'keepalt 0r ' . fnameescape(l:template_path)
|
||||
" If template_file is an absolute path, use it as-is. This is to support
|
||||
" overrides pointing to templates outside of the vim-go plugin dir
|
||||
if fnamemodify(l:template_file, ':p') != l:template_file
|
||||
let l:template_file = go#util#Join(l:root_dir, "templates", l:template_file)
|
||||
endif
|
||||
|
||||
silent exe 'keepalt 0r ' . fnameescape(l:template_file)
|
||||
endif
|
||||
else
|
||||
let l:content = printf("package %s", l:package_name)
|
||||
|
@ -96,6 +96,13 @@ function! s:on_stdout(job_id, data, event) dict abort
|
||||
endfunction
|
||||
|
||||
function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
" change to directory where test were run. if we do not do this
|
||||
" the quickfix items will have the incorrect paths.
|
||||
" see: https://github.com/fatih/vim-go/issues/2400
|
||||
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||
let l:dir = getcwd()
|
||||
execute l:cd . fnameescape(expand("%:p:h"))
|
||||
|
||||
let l:winid = win_getid(winnr())
|
||||
call win_gotoid(self.winid)
|
||||
let l:listtype = go#list#Type("_term")
|
||||
@ -130,8 +137,10 @@ function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
endif
|
||||
|
||||
" close terminal; we don't need it anymore
|
||||
call win_gotoid(self.termwinid)
|
||||
close!
|
||||
if go#config#TermCloseOnExit()
|
||||
call win_gotoid(self.termwinid)
|
||||
close!
|
||||
endif
|
||||
|
||||
if self.bang
|
||||
call win_gotoid(l:winid)
|
||||
@ -140,6 +149,21 @@ function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
|
||||
call win_gotoid(self.winid)
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
|
||||
" change back to original working directory
|
||||
execute l:cd l:dir
|
||||
endfunction
|
||||
|
||||
function! go#term#ToggleCloseOnExit() abort
|
||||
if go#config#TermCloseOnExit()
|
||||
call go#config#SetTermCloseOnExit(0)
|
||||
call go#util#EchoProgress("term close on exit disabled")
|
||||
return
|
||||
endif
|
||||
|
||||
call go#config#SetTermCloseOnExit(1)
|
||||
call go#util#EchoProgress("term close on exit enabled")
|
||||
return
|
||||
endfunction
|
||||
|
||||
" restore Vi compatibility settings
|
||||
|
@ -22,6 +22,7 @@ func! Test_GoTermNewMode()
|
||||
call assert_equal(actual, l:expected)
|
||||
|
||||
finally
|
||||
sleep 50m
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
endfunc
|
||||
@ -46,6 +47,7 @@ func! Test_GoTermNewMode_SplitRight()
|
||||
call assert_equal(actual, l:expected)
|
||||
|
||||
finally
|
||||
sleep 50m
|
||||
call delete(l:tmp, 'rf')
|
||||
set nosplitright
|
||||
endtry
|
||||
|
@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func ExampleHelloWorld() {
|
||||
fmt.Println("Hello, World")
|
||||
// Output: What's shakin
|
||||
}
|
@ -32,6 +32,7 @@ function! go#test#Test(bang, compile, ...) abort
|
||||
|
||||
if go#config#TermEnabled()
|
||||
call go#term#new(a:bang, ["go"] + args, s:errorformat())
|
||||
return
|
||||
endif
|
||||
|
||||
if go#util#has_job()
|
||||
@ -166,9 +167,17 @@ function! s:errorformat() abort
|
||||
let format .= ",%-G" . indent . "%#--- PASS: %.%#"
|
||||
|
||||
" Match failure lines.
|
||||
"
|
||||
|
||||
" Example failures start with '--- FAIL: ', followed by the example name
|
||||
" followed by a space , followed by the duration of the example in
|
||||
" parantheses. They aren't nested, though, so don't check for indentation.
|
||||
" The errors from them also aren't indented and don't report file location
|
||||
" or line numbers, so those won't show up. This will at least let the user
|
||||
" know which example failed, though.
|
||||
let format .= ',%G--- FAIL: %\\%(Example%\\)%\\@=%m (%.%#)'
|
||||
|
||||
" Test failures start with '--- FAIL: ', followed by the test name followed
|
||||
" by a space the duration of the test in parentheses
|
||||
" by a space, followed by the duration of the test in parentheses.
|
||||
"
|
||||
" e.g.:
|
||||
" '--- FAIL: TestSomething (0.00s)'
|
||||
|
@ -66,9 +66,9 @@ endfunc
|
||||
func! Test_GoTestShowName() abort
|
||||
let expected = [
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld'},
|
||||
\ {'lnum': 6, 'bufnr': 7, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
|
||||
\ {'lnum': 6, 'bufnr': 8, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'so long'},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'TestHelloWorld/sub'},
|
||||
\ {'lnum': 9, 'bufnr': 7, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
|
||||
\ {'lnum': 9, 'bufnr': 8, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
|
||||
\ ]
|
||||
|
||||
let g:go_test_show_name=1
|
||||
@ -78,20 +78,27 @@ endfunc
|
||||
|
||||
func! Test_GoTestVet() abort
|
||||
let expected = [
|
||||
\ {'lnum': 6, 'bufnr': 10, 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has 0 args'},
|
||||
\ {'lnum': 6, 'bufnr': 11, 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has 0 args'},
|
||||
\ ]
|
||||
call s:test('veterror/veterror.go', expected)
|
||||
endfunc
|
||||
|
||||
func! Test_GoTestTestCompilerError() abort
|
||||
let expected = [
|
||||
\ {'lnum': 10, 'bufnr': 8, 'col': 16, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'cannot use r (type struct {}) as type io.Reader in argument to ioutil.ReadAll:'},
|
||||
\ {'lnum': 10, 'bufnr': 9, 'col': 16, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'cannot use r (type struct {}) as type io.Reader in argument to ioutil.ReadAll:'},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'struct {} does not implement io.Reader (missing Read method)'}
|
||||
\ ]
|
||||
|
||||
call s:test('testcompilerror/testcompilerror_test.go', expected)
|
||||
endfunc
|
||||
|
||||
func! Test_GoTestExample() abort
|
||||
let expected = [
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'ExampleHelloWorld'}
|
||||
\ ]
|
||||
call s:test('example/example_test.go', expected)
|
||||
endfunc
|
||||
|
||||
func! s:test(file, expected, ...) abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
|
||||
silent exe 'e ' . $GOPATH . '/src/' . a:file
|
||||
|
@ -115,7 +115,15 @@ endfunction
|
||||
|
||||
function! go#tool#DescribeBalloon()
|
||||
let l:fname = fnamemodify(bufname(v:beval_bufnr), ':p')
|
||||
call go#lsp#Hover(l:fname, v:beval_lnum, v:beval_col, funcref('s:balloon', []))
|
||||
|
||||
let l:winid = win_getid()
|
||||
|
||||
call win_gotoid(bufwinid(v:beval_bufnr))
|
||||
|
||||
let [l:line, l:col] = go#lsp#lsp#Position(v:beval_lnum, v:beval_col)
|
||||
call go#lsp#Hover(l:fname, l:line, l:col, funcref('s:balloon', []))
|
||||
|
||||
call win_gotoid(l:winid)
|
||||
return ''
|
||||
endfunction
|
||||
|
||||
|
@ -36,15 +36,9 @@ function! go#util#Join(...) abort
|
||||
endfunction
|
||||
|
||||
" IsWin returns 1 if current OS is Windows or 0 otherwise
|
||||
" Note that has('win32') is always 1 when has('win64') is 1, so has('win32') is enough.
|
||||
function! go#util#IsWin() abort
|
||||
let win = ['win16', 'win32', 'win64', 'win95']
|
||||
for w in win
|
||||
if (has(w))
|
||||
return 1
|
||||
endif
|
||||
endfor
|
||||
|
||||
return 0
|
||||
return has('win32')
|
||||
endfunction
|
||||
|
||||
" IsMac returns 1 if current OS is macOS or 0 otherwise.
|
||||
@ -468,7 +462,7 @@ function! go#util#tempdir(prefix) abort
|
||||
endif
|
||||
|
||||
" Not great randomness, but "good enough" for our purpose here.
|
||||
let l:rnd = sha256(printf('%s%s', localtime(), fnamemodify(bufname(''), ":p")))
|
||||
let l:rnd = sha256(printf('%s%s', reltimestr(reltime()), fnamemodify(bufname(''), ":p")))
|
||||
let l:tmp = printf("%s/%s%s", l:dir, a:prefix, l:rnd)
|
||||
call mkdir(l:tmp, 'p', 0700)
|
||||
return l:tmp
|
||||
@ -557,7 +551,9 @@ function! go#util#SetEnv(name, value) abort
|
||||
let l:remove = 1
|
||||
endif
|
||||
|
||||
call execute('let $' . a:name . ' = "' . a:value . '"')
|
||||
" wrap the value in single quotes so that it will work on windows when there
|
||||
" are backslashes present in the value (e.g. $PATH).
|
||||
call execute('let $' . a:name . " = '" . a:value . "'")
|
||||
|
||||
if l:remove
|
||||
function! s:remove(name) abort
|
||||
|
@ -12,6 +12,9 @@ set cpo&vim
|
||||
" The full path to the created directory is returned, it is the caller's
|
||||
" responsibility to clean that up!
|
||||
fun! gotest#write_file(path, contents) abort
|
||||
if go#util#has_job()
|
||||
call go#lsp#CleanWorkspaces()
|
||||
endif
|
||||
let l:dir = go#util#tempdir("vim-go-test/testrun/")
|
||||
let $GOPATH .= ':' . l:dir
|
||||
let l:full_path = l:dir . '/src/' . a:path
|
||||
@ -19,15 +22,23 @@ fun! gotest#write_file(path, contents) abort
|
||||
call mkdir(fnamemodify(l:full_path, ':h'), 'p')
|
||||
call writefile(a:contents, l:full_path)
|
||||
exe 'cd ' . l:dir . '/src'
|
||||
|
||||
if go#util#has_job()
|
||||
call go#lsp#AddWorkspaceDirectory(fnamemodify(l:full_path, ':p:h'))
|
||||
endif
|
||||
|
||||
silent exe 'e! ' . a:path
|
||||
|
||||
" Set cursor.
|
||||
let l:lnum = 1
|
||||
for l:line in a:contents
|
||||
let l:m = match(l:line, "\x1f")
|
||||
let l:m = stridx(l:line, "\x1f")
|
||||
if l:m > -1
|
||||
call setpos('.', [0, l:lnum, l:m, 0])
|
||||
let l:byte = line2byte(l:lnum) + l:m
|
||||
exe 'goto '. l:byte
|
||||
call setline('.', substitute(getline('.'), "\x1f", '', ''))
|
||||
silent noautocmd w!
|
||||
|
||||
break
|
||||
endif
|
||||
|
||||
@ -42,6 +53,9 @@ endfun
|
||||
" The file will be copied to a new GOPATH-compliant temporary directory and
|
||||
" loaded as the current buffer.
|
||||
fun! gotest#load_fixture(path) abort
|
||||
if go#util#has_job()
|
||||
call go#lsp#CleanWorkspaces()
|
||||
endif
|
||||
let l:dir = go#util#tempdir("vim-go-test/testrun/")
|
||||
let $GOPATH .= ':' . l:dir
|
||||
let l:full_path = l:dir . '/src/' . a:path
|
||||
@ -51,6 +65,9 @@ fun! gotest#load_fixture(path) abort
|
||||
silent exe 'noautocmd e ' . a:path
|
||||
silent exe printf('read %s/test-fixtures/%s', g:vim_go_root, a:path)
|
||||
silent noautocmd w!
|
||||
if go#util#has_job()
|
||||
call go#lsp#AddWorkspaceDirectory(fnamemodify(l:full_path, ':p:h'))
|
||||
endif
|
||||
|
||||
return l:dir
|
||||
endfun
|
||||
|
@ -94,26 +94,26 @@ For Pathogen or Vim |packages|, just clone the repo. For other plugin managers
|
||||
you may also need to add the lines to your vimrc to execute the plugin
|
||||
manager's install command.
|
||||
|
||||
* Vim 8 |packages|
|
||||
>
|
||||
* Vim 8 |packages| >
|
||||
|
||||
git clone https://github.com/fatih/vim-go.git \
|
||||
~/.vim/pack/plugins/start/vim-go
|
||||
|
||||
<
|
||||
* https://github.com/tpope/vim-pathogen >
|
||||
|
||||
git clone https://github.com/fatih/vim-go.git ~/.vim/bundle/vim-go
|
||||
<
|
||||
* https://github.com/junegunn/vim-plug >
|
||||
|
||||
Plug 'fatih/vim-go'
|
||||
|
||||
Plug 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }
|
||||
<
|
||||
* https://github.com/Shougo/neobundle.vim >
|
||||
|
||||
NeoBundle 'fatih/vim-go'
|
||||
<
|
||||
* https://github.com/gmarik/vundle >
|
||||
|
||||
Plugin 'fatih/vim-go', { 'do': ':GoUpdateBinaries' }
|
||||
Plugin 'fatih/vim-go'
|
||||
<
|
||||
* Manual (not recommended) >
|
||||
|
||||
@ -376,8 +376,8 @@ CTRL-t
|
||||
:GoInfo
|
||||
Show type information about the identifier under the cursor. For example
|
||||
putting it above a function call is going to show the full function
|
||||
signature. By default it uses `gocode` to get the type informations. To
|
||||
change the underlying tool from `gocode` to another tool, see
|
||||
signature. By default it uses `gopls` to get the type informations. To
|
||||
change the underlying tool from `gopls` to another tool, see
|
||||
|'g:go_info_mode'|.
|
||||
|
||||
|
||||
@ -536,7 +536,7 @@ CTRL-t
|
||||
|
||||
|
||||
*:GoGuruScope*
|
||||
:GoGuruScope [pattern] [pattern2] ... [patternN]
|
||||
:GoGuruScope [pattern] ...
|
||||
|
||||
Changes the custom |'g:go_guru_scope'| setting and overrides it with the
|
||||
given package patterns. The custom scope is cleared (unset) if `""` is
|
||||
@ -906,6 +906,16 @@ CTRL-t
|
||||
tries to preserve cursor position and avoids replacing the buffer with
|
||||
stderr output.
|
||||
|
||||
*:GoAddWorkspace*
|
||||
:GoAddWorkspace [dir] ...
|
||||
|
||||
Add directories to the `gopls` workspace.
|
||||
|
||||
*:GoLSPDebugBrowser*
|
||||
:GoLSPDebugBrowser
|
||||
|
||||
Open a browser to see gopls debugging information.
|
||||
|
||||
==============================================================================
|
||||
MAPPINGS *go-mappings*
|
||||
|
||||
@ -1204,6 +1214,14 @@ balloonexpr`.
|
||||
==============================================================================
|
||||
SETTINGS *go-settings*
|
||||
|
||||
*'g:go_version_warning'*
|
||||
|
||||
Enable warning when using an unsupported version of Vim. By default it is
|
||||
enabled.
|
||||
>
|
||||
let g:go_version_warning = 1
|
||||
<
|
||||
|
||||
*'g:go_code_completion_enabled'*
|
||||
|
||||
Enable code completion with |'omnifunc'|. By default it is enabled.
|
||||
@ -1228,9 +1246,9 @@ set to 10 seconds . >
|
||||
<
|
||||
*'g:go_play_browser_command'*
|
||||
|
||||
Browser to use for |:GoPlay| or |:GoDocBrowser|. The url must be added with
|
||||
`%URL%`, and it's advisable to include `&` to make sure the shell returns. For
|
||||
example:
|
||||
Browser to use for |:GoPlay|, |:GoDocBrowser|, and |:GoLSPDebugBrowser|. The
|
||||
url must be added with `%URL%`, and it's advisable to include `&` to make sure
|
||||
the shell returns. For example:
|
||||
>
|
||||
let g:go_play_browser_command = 'firefox-developer %URL% &'
|
||||
<
|
||||
@ -1259,11 +1277,11 @@ updated. By default it's disabled. The delay can be configured with the
|
||||
*'g:go_info_mode'*
|
||||
|
||||
Use this option to define the command to be used for |:GoInfo|. By default
|
||||
`gocode` is being used as it's the fastest option. But one might also use
|
||||
`gopls` or `guru` as they cover more cases and are more accurate. Current
|
||||
valid options are: `[gocode, guru, gopls]` >
|
||||
|
||||
let g:go_info_mode = 'gocode'
|
||||
`gopls` is used, because it is the fastest and is known to be highly accurate.
|
||||
One might also use `guru` for its accuracy or `gocode` for its performance.
|
||||
Valid options are `gocode`, `gopls`, and `guru`.
|
||||
>
|
||||
let g:go_info_mode = 'gopls'
|
||||
<
|
||||
*'g:go_auto_sameids'*
|
||||
|
||||
@ -1375,10 +1393,11 @@ a private internal service. Default is 'https://godoc.org'.
|
||||
*'g:go_def_mode'*
|
||||
|
||||
Use this option to define the command to be used for |:GoDef|. By default
|
||||
`guru` is being used as it covers all edge cases. But one might also use
|
||||
`godef` as it's faster. Current valid options are: `[guru, godef, gopls]` >
|
||||
|
||||
let g:go_def_mode = 'guru'
|
||||
`gopls` is used, because it is the fastest. One might also use `guru` for its
|
||||
accuracy or `godef` for its performance. Valid options are `godef`, `gopls`,
|
||||
and `guru`.
|
||||
>
|
||||
let g:go_def_mode = 'gopls'
|
||||
<
|
||||
*'g:go_def_mapping_enabled'*
|
||||
|
||||
@ -1621,6 +1640,13 @@ according to |'g:go_term_mode'|, otherwise it will run them in the background
|
||||
just like `:GoBuild`. By default it is disabled.
|
||||
>
|
||||
let g:go_term_enabled = 0
|
||||
<
|
||||
*'g:go_term_close_on_exit'*
|
||||
|
||||
This option is Neovim only. If set to 1 it closes the terminal after the
|
||||
command run in it exits. By default it is enabled.
|
||||
>
|
||||
let g:go_term_close_on_exit = 1
|
||||
<
|
||||
*'g:go_alternate_mode'*
|
||||
|
||||
@ -1676,7 +1702,8 @@ Specifies whether `gocode` should use a different socket type. By default
|
||||
When a new Go file is created, vim-go automatically fills the buffer content
|
||||
with a Go code template. By default, the templates under the `templates`
|
||||
folder are used. This can be changed with the |'g:go_template_file'| and
|
||||
|'g:go_template_test_file'| settings.
|
||||
|'g:go_template_test_file'| settings to either use a different file in the
|
||||
same `templates` folder, or to use a file stored elsewhere.
|
||||
|
||||
If the new file is created in an already prepopulated package (with other Go
|
||||
files), in this case a Go code template with only the Go package declaration
|
||||
@ -1691,17 +1718,23 @@ By default it is enabled.
|
||||
<
|
||||
*'g:go_template_file'*
|
||||
|
||||
Specifies the file under the `templates` folder that is used if a new Go file
|
||||
is created. Checkout |'g:go_template_autocreate'| for more info. By default
|
||||
the `hello_world.go` file is used.
|
||||
Specifies either the file under the `templates` folder that is used if a new
|
||||
Go file is created. Checkout |'g:go_template_autocreate'| for more info. By
|
||||
default the `hello_world.go` file is used.
|
||||
|
||||
This variable can be set to an absolute path, so the template files don't have
|
||||
to be stored inside the vim-go directory structure. Useful when you want to
|
||||
use different templates for different projects.
|
||||
>
|
||||
let g:go_template_file = "hello_world.go"
|
||||
<
|
||||
*'g:go_template_test_file'*
|
||||
|
||||
Specifies the file under the `templates` folder that is used if a new Go test
|
||||
file is created. Checkout |'g:go_template_autocreate'| for more info. By
|
||||
default the `hello_world_test.go` file is used.
|
||||
Like with |'g:go_template_file'|, this specifies the file to use for test
|
||||
tempaltes. The template file should be under the `templates` folder,
|
||||
alternatively absolute paths can be used, too. Checkout
|
||||
|'g:go_template_autocreate'| for more info. By default, the
|
||||
`hello_world_test.go` file is used.
|
||||
>
|
||||
let g:go_template_test_file = "hello_world_test.go"
|
||||
<
|
||||
@ -1788,7 +1821,10 @@ Currently accepted values:
|
||||
debugger-state Expose debugger state in 'g:go_debug_diag'.
|
||||
debugger-commands Echo communication between vim-go and `dlv`; requests and
|
||||
responses are recorded in `g:go_debug_commands`.
|
||||
lsp Record lsp requests and responses in g:go_lsp_log.
|
||||
lsp Echo communication between vim-go and `gopls`. All
|
||||
communication is shown in a dedicated window. When
|
||||
enabled before gopls is started, |:GoLSPDebugBrowser| can
|
||||
be used to open a browser window to help debug gopls.
|
||||
>
|
||||
let g:go_debug = []
|
||||
<
|
||||
@ -2027,8 +2063,7 @@ rest of the commands and mappings become available after starting debug mode.
|
||||
* Make the `:GoDebug*` commands and `(go-debug-*)` mappings available.
|
||||
|
||||
The directory of the current buffer is used if [pkg] is empty. Any other
|
||||
arguments will be passed to the program. When [pkg] is relative, it will
|
||||
be interpreted relative to the directory of the current buffer.
|
||||
arguments will be passed to the program.
|
||||
|
||||
Use |:GoDebugStop| to stop `dlv` and exit debugging mode.
|
||||
|
||||
@ -2183,6 +2218,22 @@ Highlight the current line and breakpoints in the debugger.
|
||||
==============================================================================
|
||||
FAQ TROUBLESHOOTING *go-troubleshooting*
|
||||
|
||||
How do I troubleshoot problems?~
|
||||
|
||||
One of the best ways to understand what vim-go is doing and the output from
|
||||
the tools to which it delegates is to use leverage the features described in
|
||||
|'g:go_debug'|.
|
||||
|
||||
Completion and other functions that use `gopls` don't work~
|
||||
|
||||
Vim-go is heavily reliant on `gopls` for completion and other functionality.
|
||||
Many of the features that use `gopls` (e.g. completion, jumping to
|
||||
definitions, showing identifier information, et al.) can be configured to
|
||||
delegate to other tools. e.g. completion via |'omnifunc'|, |'g:go_info_mode'|
|
||||
and |'g:go_def_mode'| can be set to use other tools for now (though some of
|
||||
the alternatives to `gopls` are effectively at their end of life and support
|
||||
for them from within vim-go may be removed soon).
|
||||
|
||||
I get "Unknown function: go#config#..." error when I open a Go file.~
|
||||
|
||||
This often happens to vim-polyglot users when new config options are added to
|
||||
|
@ -116,4 +116,11 @@ command! -nargs=0 GoReportGitHubIssue call go#issue#New()
|
||||
" -- iferr
|
||||
command! -nargs=0 GoIfErr call go#iferr#Generate()
|
||||
|
||||
" -- lsp
|
||||
command! -nargs=+ -complete=dir GoAddWorkspace call go#lsp#AddWorkspaceDirectory(<f-args>)
|
||||
command! -nargs=0 GoLSPDebugBrowser call go#lsp#DebugBrowser()
|
||||
|
||||
" -- term
|
||||
command! GoToggleTermCloseOnExit call go#term#ToggleCloseOnExit()
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
@ -151,7 +151,7 @@ if err := ${1:condition}; err != nil {
|
||||
endsnippet
|
||||
|
||||
# error snippet
|
||||
snippet errn "Error return " !b
|
||||
snippet errn "Error return" !b
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -326,7 +326,7 @@ endsnippet
|
||||
# struct
|
||||
snippet st "type T struct { ... }"
|
||||
type ${1:Type} struct {
|
||||
${0}
|
||||
${0}
|
||||
}
|
||||
endsnippet
|
||||
|
||||
|
@ -20,19 +20,19 @@ function! s:checkVersion() abort
|
||||
let l:unsupported = 0
|
||||
if go#config#VersionWarning() != 0
|
||||
if has('nvim')
|
||||
let l:unsupported = !has('nvim-0.3.1')
|
||||
let l:unsupported = !has('nvim-0.3.2')
|
||||
else
|
||||
let l:unsupported = (v:version < 704 || (v:version == 704 && !has('patch2009')))
|
||||
endif
|
||||
|
||||
if l:unsupported == 1
|
||||
echohl Error
|
||||
echom "vim-go requires Vim 7.4.2009 or Neovim 0.3.1, but you're using an older version."
|
||||
echom "vim-go requires Vim 7.4.2009 or Neovim 0.3.2, but you're using an older version."
|
||||
echom "Please update your Vim for the best vim-go experience."
|
||||
echom "If you really want to continue you can set this to make the error go away:"
|
||||
echom " let g:go_version_warning = 0"
|
||||
echom "Note that some features may error out or behave incorrectly."
|
||||
echom "Please do not report bugs unless you're using Vim 7.4.2009 or newer or Neovim 0.3.1."
|
||||
echom "Please do not report bugs unless you're using Vim 7.4.2009 or newer or Neovim 0.3.2."
|
||||
echohl None
|
||||
|
||||
" Make sure people see this.
|
||||
@ -56,7 +56,7 @@ let s:packages = {
|
||||
\ 'gogetdoc': ['github.com/zmb3/gogetdoc'],
|
||||
\ 'goimports': ['golang.org/x/tools/cmd/goimports'],
|
||||
\ 'golint': ['golang.org/x/lint/golint'],
|
||||
\ 'gopls': ['golang.org/x/tools/cmd/gopls'],
|
||||
\ 'gopls': ['golang.org/x/tools/gopls@latest', {}, {'after': function('go#lsp#Restart', [])}],
|
||||
\ 'gometalinter': ['github.com/alecthomas/gometalinter'],
|
||||
\ 'golangci-lint': ['github.com/golangci/golangci-lint/cmd/golangci-lint'],
|
||||
\ 'gomodifytags': ['github.com/fatih/gomodifytags'],
|
||||
@ -104,9 +104,6 @@ function! s:GoInstallBinaries(updateBinaries, ...)
|
||||
" vim's executable path is looking in PATH so add our go_bin path to it
|
||||
let Restore_path = go#util#SetEnv('PATH', go_bin_path . go#util#PathListSep() . $PATH)
|
||||
|
||||
" GO111MODULE must be off to install golanci-lint and gometalinter
|
||||
let Restore_modules = go#util#SetEnv('GO111MODULE', 'off')
|
||||
|
||||
" when shellslash is set on MS-* systems, shellescape puts single quotes
|
||||
" around the output string. cmd on Windows does not handle single quotes
|
||||
" correctly. Unsetting shellslash forces shellescape to use double quotes
|
||||
@ -117,10 +114,7 @@ function! s:GoInstallBinaries(updateBinaries, ...)
|
||||
set noshellslash
|
||||
endif
|
||||
|
||||
let l:dl_cmd = ['go', 'get', '-v', '-d']
|
||||
if get(g:, "go_get_update", 1) != 0
|
||||
let l:dl_cmd += ['-u']
|
||||
endif
|
||||
let l:get_base_cmd = ['go', 'get', '-v']
|
||||
|
||||
" Filter packages from arguments (if any).
|
||||
let l:packages = {}
|
||||
@ -142,51 +136,93 @@ function! s:GoInstallBinaries(updateBinaries, ...)
|
||||
let l:platform = 'windows'
|
||||
endif
|
||||
|
||||
for [binary, pkg] in items(l:packages)
|
||||
let l:importPath = pkg[0]
|
||||
let l:oldmore = &more
|
||||
let &more = 0
|
||||
|
||||
let l:run_cmd = copy(l:dl_cmd)
|
||||
if len(l:pkg) > 1 && get(l:pkg[1], l:platform, '') isnot ''
|
||||
let l:run_cmd += get(l:pkg[1], l:platform, '')
|
||||
endif
|
||||
for [l:binary, l:pkg] in items(l:packages)
|
||||
let l:importPath = l:pkg[0]
|
||||
|
||||
let bin_setting_name = "go_" . binary . "_bin"
|
||||
" TODO(bc): how to support this with modules? Do we have to clone and then
|
||||
" install manually? Probably not. I suspect that we can just use GOPATH
|
||||
" mode and then do the legacy method.
|
||||
let bin_setting_name = "go_" . l:binary . "_bin"
|
||||
|
||||
if exists("g:{bin_setting_name}")
|
||||
let bin = g:{bin_setting_name}
|
||||
else
|
||||
if go#util#IsWin()
|
||||
let bin = binary . '.exe'
|
||||
let bin = l:binary . '.exe'
|
||||
else
|
||||
let bin = binary
|
||||
let bin = l:binary
|
||||
endif
|
||||
endif
|
||||
|
||||
if !executable(bin) || a:updateBinaries == 1
|
||||
if a:updateBinaries == 1
|
||||
echo "vim-go: Updating " . binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
|
||||
echo "vim-go: Updating " . l:binary . ". Reinstalling ". importPath . " to folder " . go_bin_path
|
||||
else
|
||||
echo "vim-go: ". binary ." not found. Installing ". importPath . " to folder " . go_bin_path
|
||||
echo "vim-go: ". l:binary ." not found. Installing ". importPath . " to folder " . go_bin_path
|
||||
endif
|
||||
|
||||
" first download the binary
|
||||
let [l:out, l:err] = go#util#Exec(l:run_cmd + [l:importPath])
|
||||
if l:err
|
||||
echom "Error downloading " . l:importPath . ": " . l:out
|
||||
if l:importPath =~ "@"
|
||||
let Restore_modules = go#util#SetEnv('GO111MODULE', 'on')
|
||||
let l:tmpdir = go#util#tempdir('vim-go')
|
||||
let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||
let l:dir = getcwd()
|
||||
try
|
||||
execute l:cd . fnameescape(l:tmpdir)
|
||||
let l:get_cmd = copy(l:get_base_cmd)
|
||||
|
||||
" first download the binary
|
||||
let [l:out, l:err] = go#util#Exec(l:get_cmd + [l:importPath])
|
||||
if l:err
|
||||
echom "Error installing " . l:importPath . ": " . l:out
|
||||
endif
|
||||
|
||||
call call(Restore_modules, [])
|
||||
finally
|
||||
execute l:cd . fnameescape(l:dir)
|
||||
endtry
|
||||
call call(Restore_modules, [])
|
||||
else
|
||||
let l:get_cmd = copy(l:get_base_cmd)
|
||||
let l:get_cmd += ['-d']
|
||||
if get(g:, "go_get_update", 1) != 0
|
||||
let l:get_cmd += ['-u']
|
||||
endif
|
||||
|
||||
" GO111MODULE must be off to install gometalinter.
|
||||
let Restore_modules = go#util#SetEnv('GO111MODULE', 'off')
|
||||
|
||||
" first download the binary
|
||||
let [l:out, l:err] = go#util#Exec(l:get_cmd + [l:importPath])
|
||||
if l:err
|
||||
echom "Error downloading " . l:importPath . ": " . l:out
|
||||
endif
|
||||
|
||||
" and then build and install it
|
||||
let l:build_cmd = ['go', 'build', '-o', go_bin_path . go#util#PathSep() . bin, l:importPath]
|
||||
if len(l:pkg) > 1 && get(l:pkg[1], l:platform, '') isnot ''
|
||||
let l:build_cmd += get(l:pkg[1], l:platform, '')
|
||||
endif
|
||||
|
||||
let [l:out, l:err] = go#util#Exec(l:build_cmd)
|
||||
if l:err
|
||||
echom "Error installing " . l:importPath . ": " . l:out
|
||||
endif
|
||||
|
||||
|
||||
call call(Restore_modules, [])
|
||||
endif
|
||||
|
||||
" and then build and install it
|
||||
let l:build_cmd = ['go', 'build', '-o', go_bin_path . go#util#PathSep() . bin, l:importPath]
|
||||
let [l:out, l:err] = go#util#Exec(l:build_cmd)
|
||||
if l:err
|
||||
echom "Error installing " . l:importPath . ": " . l:out
|
||||
if len(l:pkg) > 2
|
||||
call call(get(l:pkg[2], 'after', function('s:noop', [])), [])
|
||||
endif
|
||||
endif
|
||||
endfor
|
||||
|
||||
" restore back!
|
||||
call call(Restore_path, [])
|
||||
call call(Restore_modules, [])
|
||||
|
||||
if resetshellslash
|
||||
set shellslash
|
||||
@ -197,6 +233,8 @@ function! s:GoInstallBinaries(updateBinaries, ...)
|
||||
else
|
||||
call go#util#EchoInfo('installing finished!')
|
||||
endif
|
||||
|
||||
let &more = l:oldmore
|
||||
endfunction
|
||||
|
||||
" CheckBinaries checks if the necessary binaries to install the Go tool
|
||||
|
@ -30,7 +30,7 @@ case "$vim" in
|
||||
|
||||
"nvim")
|
||||
# Use latest stable version.
|
||||
tag="v0.3.1"
|
||||
tag="v0.3.2"
|
||||
giturl="https://github.com/neovim/neovim"
|
||||
;;
|
||||
|
||||
@ -62,7 +62,7 @@ cd "$srcdir"
|
||||
if [ "$1" = "nvim" ]; then
|
||||
|
||||
# TODO: Use macOS binaries on macOS
|
||||
curl -Ls https://github.com/neovim/neovim/releases/download/nightly/nvim-linux64.tar.gz |
|
||||
curl -Ls https://github.com/neovim/neovim/releases/download/$tag/nvim-linux64.tar.gz |
|
||||
tar xzf - -C /tmp/vim-go-test/
|
||||
mv /tmp/vim-go-test/nvim-linux64 /tmp/vim-go-test/nvim-install
|
||||
mkdir -p "$installdir/share/nvim/runtime/pack/vim-go/start"
|
||||
|
@ -64,6 +64,12 @@ for s:test in sort(s:tests)
|
||||
endif
|
||||
try
|
||||
exe 'call ' . s:test
|
||||
" sleep to give events a chance to be processed. This is especially
|
||||
" important for the LSP code to have a chance to run before Vim exits, in
|
||||
" order to avoid errors trying to write to the gopls channels since Vim
|
||||
" would otherwise stop gopls before the event handlers were run and result
|
||||
" in 'stream closed' errors when the events were run _after_ gopls exited.
|
||||
sleep 50m
|
||||
catch
|
||||
let v:errors += [v:exception]
|
||||
endtry
|
||||
@ -76,17 +82,17 @@ for s:test in sort(s:tests)
|
||||
let s:elapsed_time = substitute(reltimestr(reltime(s:started)), '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||
let s:done += 1
|
||||
|
||||
call s:logmessages()
|
||||
|
||||
if len(v:errors) > 0
|
||||
let s:fail += 1
|
||||
call add(s:logs, printf("--- FAIL %s (%ss)", s:test[:-3], s:elapsed_time))
|
||||
call s:logmessages()
|
||||
call extend(s:logs, map(v:errors, '" ". v:val'))
|
||||
|
||||
" Reset so we can capture failures of the next test.
|
||||
let v:errors = []
|
||||
else
|
||||
if g:test_verbose is 1
|
||||
call s:logmessages()
|
||||
call add(s:logs, printf("--- PASS %s (%ss)", s:test[:-3], s:elapsed_time))
|
||||
endif
|
||||
endif
|
||||
|
Reference in New Issue
Block a user