Squashed 'vim/bundle/go/' changes from bf390af42..75210b2ba
75210b2ba New release: v1.17 723e92485 Merge pull request #1743 from Carpetsmoker/debug-err 1c308cb7b Show debug errors as errors and not progress 73c88fd53 Fix [[ and ]] jumping 3b5b1480f Add :GoDebugTest (#1728) b4eed1e17 redraw before showing progress message only (#1736) e0ba424d3 Merge pull request #1733 from bhcleek/fix-coverage-prompt b2591c356 redraw before echo baaa1788d Merge pull request #1731 from bhcleek/hide-codecov-comment 50fc7e048 Add codecov.io configuration 37e2587aa update CHANGELOG.md for #1725 bb80ad523 Merge pull request #1725 from bhcleek/fix-term-enabled-behavior bca02bcbd update CHANGELOG.md for #1713 8f72dc96a Merge pull request #1713 from bhcleek/cache-import-path 426621fd5 fix splitright behavior when using a terminal buffer afb8e97d2 remove startinsert and stopinsert pair 702b79560 remove stderr callback f80622f39 Merge pull request #1724 from fatih/list-clear 8d040e197 list: add go#list#Close() 16e932cce list: make Clean() more useful 946330869 Merge pull request #1722 from bhcleek/fix-unlikely-term-race b43a68c88 remove redundant changelog entry 800f2946b close the correct location list 2dcb2d228 update CHANGELOG.md for #1716 and #1721 f16f546cb Merge pull request #1721 from bhcleek/fix-terminal-races fe6008e45 Merge pull request #1716 from bhcleek/fix-go-build-error-parse 45cc21201 Update CHANGELOG.md b58082312 Merge pull request #1701 from fatih/remove-install a26578790 Fix various race conditions when using terminal windows f916e1ee5 parse go build errors correctly in vim8 and neovim dd9b635f7 add test for GoBuild 49f560f71 use go#list#ParseFormat() to parse go vet output 6d68c01b3 add test for GoVet f2b67eaa5 cache import path lookups results d2b0a234f update changelog for #1697 dcbfdf3ab Merge pull request #1697 from bhcleek/async-info 24991559a add comments to explain offset calculations 945b63607 Make sure :GoDebugBreakpoint works after :GoDebugStop (#1706) 1bdc75bfc More reliable way to keep track if the program is running (#1699) 8c6a186da update CHANGELOG.md for #1678 5bc8d9edb Merge pull request #1678 from entombedvirus/rohith/async-guru-neovim 2447029b7 cmd: go 1.10 maintains a build cache now ba2a1e81f neovim: invoke guru functions via async api eaef6f793 Do not steal focus when linting on save in vim8. 1292d1545 Update ChangeLog a9fed3289 add fmt.Errorf() snippet in UltiSnips (#1677) a7dd67d18 Keep current alternate-file name while reading templates (#1675) 01bea987a fix path separator of minisnip_dir (#1698) 8c83eb568 always use gocode's vim format fb2986d8a cancel pending async info job when starting another b237f52ef get type info from gocode asynchronously d9d2d27b2 scope completions variable to autoload/go/complete.vim 2274cb5c5 rename preargs to options 4f9c2e9be Changelog.md: make debugger support more prominent 6cc4fd896 Update ChangeLog 52c6da20e add GoDebug (#1390) 39beabec0 Merge pull request #1685 from fatih/fix-go-build-bang 54de23cc6 job: fix bang variable 6dbac34d3 Merge pull request #1683 from bhcleek/fix-async-job-closure 7ed9a827f avoid race conditions in test async jobs 8f2a66dae avoid race conditions in rename async jobs 83a25f196 avoid race conditions in lint async jobs 73a38ea38 avoid race conditions in async jobs 855c35e2d avoid race conditions in guru async jobs cc0467c3c Ensure that goPackageComment region ends after comment. (#1673) 4e871cbcd update CHANGELOG.md for #1671 6437d161d Merge pull request #1671 from bhcleek/redraw-before-gocode dc3b5011a redraw before calling gocode f72ea5c9e update CHANGELOG.md a037e3608 Merge pull request #1664 from bhcleek/empty-test-message 20d52eb64 match test errors with an empty message 7ec5b4692 Merge branch 'metalinter-autosave-list' 6de9daac4 Merge pull request #1653 from bhcleek/handle-autowriteall 18b989736 Merge pull request #1656 from bhcleek/fix-async-race 5743ce160 update documentation for :GoMetaLinterAutoSave and g:go_metalinter_autosave 5a205d3a5 Update Changelog.md 8d5e37e9d Merge pull request #1654 from bhcleek/fix-lint-press-enter aef5e202b Eliminate race conditions in go test job cc24b5508 Eliminate race conditions in gorename job aa37b4288 Eliminate race conditions in gometalinter job b63ba83b4 Eliminate race conditions in guru job 7c727d9b0 Only return callbacks from go#job#Spawn 8bd171624 fix races in go#job#Spawn callbacks 87878247a document go#job#Spawn 4b146cbdc update Test_GometaAutoSave to use location list f59234991 separate gometalinter error list use cases a3716ce61 do not prompt user to press enter after saving 561365d7f handle autowriteall the same as autowrite ffb2f4369 Merge pull request #1651 from bhcleek/reduce-path-function-scope c7316d5a5 reduce visibility of function 6c5622f44 reduce visibility of function 785f632ae update CHANGELOG.md for #1648 f455b9650 Merge pull request #1648 from bhcleek/metalinter-symmetric-enabled-disabled e3974b603 Fix handling and change name of g:go_metalinter_excludes 30d04ad0e Fix ftdetect (#1645) f12f4c81e Update Changelog.md 19eb0581f Update Changelog.md 8308c8d93 Merge pull request #1557 from Carpetsmoker/highlight_methods bf3160402 Update Changelog.md aa9ee00c8 Merge pull request #1640 from bhcleek/fix-metalinter 7702f5c92 use directory arguments given to :GoMetaLinter 4a0fd5d93 fix relative path regex for gometalinter 7cc58f2f9 add tests to verify metalinter autosave behavior 2ab8f815f Add --include for async runner too 55f1598be Active buffer relative to workdir for gometalint 80730c79c Update Changelog.md 1f675f6eb Merge pull request #1641 from bhcleek/fix-show-test-name-toggle 54c62714a Update Changelog 332965f59 Merge pull request #1639 from hokorobi/improve-snippets-abbr 31e6c2839 respect a changed g:go_test_show_name 1a7150fab snippets: improve abbr descriptions for neosnippet 3ceecc2bf support syntax highlighting for bracketed expression in fmt string (#1634) 204cf8e86 Remove unused screenshot.png (#1635) e88020390 update CHANGELOG.md 8eaa1a4a2 Merge pull request #1633 from bhcleek/test-timeout-errors 816cf4203 fix quickfix list when test duration exceeds timeout 992bed6ae Update changelog.md ab598cc22 doc.vim: small style change and remove unused function b68d13b11 Merge pull request #1629 from stamblerre/master bb9903199 update CHANGELOG.md for #1631 ffdf8001b Merge pull request #1631 from wmatusiak/master bdcfc1064 Fix: GoTestFunc dont respect go_test_timeout 8a45bec4d add additional godoc url configuration 264fe46ef Rename go_highlight_methods to go_highlight_function_calls git-subtree-dir: vim/bundle/go git-subtree-split: 75210b2baccf5dc8d19d60b6d0e3cd171e0b5829
This commit is contained in:
parent
c7496b2816
commit
2aa6230554
12
.codecov.yml
Normal file
12
.codecov.yml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
coverage:
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 1
|
||||
base: auto
|
||||
comment: false
|
||||
ignore:
|
||||
- "!autoload/go/*.vim$"
|
||||
- "autoload/go/*_test.vim$"
|
||||
101
CHANGELOG.md
101
CHANGELOG.md
@ -1,5 +1,106 @@
|
||||
## unplanned
|
||||
|
||||
## 1.17 - (March 27, 2018)
|
||||
|
||||
FEATURES:
|
||||
|
||||
* **Debugger support!** Add integrated support for the
|
||||
[`delve`](https://github.com/derekparker/delve) debugger. Use
|
||||
`:GoInstallBinaries` to install `dlv`, and see `:help go-debug` to get
|
||||
started.
|
||||
[[GH-1390]](https://github.com/fatih/vim-go/pull/1390)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
* Add descriptions to neosnippet abbrevations.
|
||||
[[GH-1639]](https://github.com/fatih/vim-go/pull/1639)
|
||||
* Show messages in the location list instead of the quickfix list when
|
||||
`gometalinter` is run automatically when saving a buffer. Whether the
|
||||
location list or quickfix list is used can be customized in the usual ways.
|
||||
[[GH-1652]](https://github.com/fatih/vim-go/pull/1652)
|
||||
* Redraw the screen before executing blocking calls to gocode.
|
||||
[[GH-1671]](https://github.com/fatih/vim-go/pull/1671)
|
||||
* Add `fe` -> `fmt.Errorf()` snippet for NeoSnippet and UltiSnippets.
|
||||
[[GH-1677]](https://github.com/fatih/vim-go/pull/1677)
|
||||
* Use the async api when calling guru from neovim.
|
||||
[[GH-1678]](https://github.com/fatih/vim-go/pull/1678)
|
||||
* Use the async api when calling gocode to get type info.
|
||||
[[GH-1697]](https://github.com/fatih/vim-go/pull/1697)
|
||||
* Cache import path lookups to improve responsiveness.
|
||||
[[GH-1713]](https://github.com/fatih/vim-go/pull/1713)
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* Create quickfix list correctly when tests timeout.
|
||||
[[GH-1633]](https://github.com/fatih/vim-go/pull/1633)
|
||||
* Apply `g:go_test_timeout` when running `:GoTestFunc`.
|
||||
[[GH-1631]](https://github.com/fatih/vim-go/pull/1631)
|
||||
* The user's configured `g:go_doc_url` variable wasn't working correctly in the
|
||||
case when the "gogetdoc" command isn't installed.
|
||||
[[GH-1629]](https://github.com/fatih/vim-go/pull/1629)
|
||||
* Highlight format specifiers with an index (e.g. `%[2]d`).
|
||||
[[GH-1634]](https://github.com/fatih/vim-go/pull/1634)
|
||||
* Respect `g:go_test_show_name` change for `:GoTest` when it changes during a
|
||||
Vim session.
|
||||
[[GH-1641]](https://github.com/fatih/vim-go/pull/1641)
|
||||
* Show `g:go_test_show_name` value for `:GoTest` failures if it's available.
|
||||
[[GH-1641]](https://github.com/fatih/vim-go/pull/1641)
|
||||
* Make sure linter errors for the file being saved are shown in vim74 and nvim.
|
||||
[[GH-1640]](https://github.com/fatih/vim-go/pull/1640)
|
||||
* Make sure only linter errors for the file being saved are shown in vim8.
|
||||
Previously, all linter errors for all files in the current file's directory
|
||||
were being shown.
|
||||
[[GH-1640]](https://github.com/fatih/vim-go/pull/1640)
|
||||
* Make sure gometalinter is run on the given directories when arguments are
|
||||
given to :GoMetaLinter.
|
||||
[[GH-1640]](https://github.com/fatih/vim-go/pull/1640)
|
||||
* Do not run disabled linters with `gometalinter`.
|
||||
[[GH-1648]](https://github.com/fatih/vim-go/pull/1648)
|
||||
* Do not prompt user to press enter after when `gometalinter` is called in
|
||||
autosave mode.
|
||||
[[GH-1654]](https://github.com/fatih/vim-go/pull/1654)
|
||||
* Fix potential race conditions when using vim8 jobs.
|
||||
[[GH-1656]](https://github.com/fatih/vim-go/pull/1656)
|
||||
* Treat `'autowriteall'` the same as `'autowrite'` when determining whether to
|
||||
write a buffer before calling some commands.
|
||||
[[GH-1653]](https://github.com/fatih/vim-go/pull/1653)
|
||||
* Show the file location of test errors when the message is empty or begins
|
||||
with a newline.
|
||||
[[GH-1664]](https://github.com/fatih/vim-go/pull/1664)
|
||||
* Fix minisnip on Windows.
|
||||
[[GH-1698]](https://github.com/fatih/vim-go/pull/1698)
|
||||
* Keep alternate filename when loading an autocreate template.
|
||||
[[GH-1675]](https://github.com/fatih/vim-go/pull/1675)
|
||||
* Parse the column number in errors correctly in vim8 and neovim.
|
||||
[[GH-1716]](https://github.com/fatih/vim-go/pull/1716)
|
||||
* Fix race conditions in the terminal handling for neovim.
|
||||
[[GH-1721]](https://github.com/fatih/vim-go/pull/1721)
|
||||
* Put the user back in the original window regardless of the value of
|
||||
`splitright` after starting a neovim terminal window.
|
||||
[[GH-1725]](https://github.com/fatih/vim-go/pull/1725)
|
||||
|
||||
BACKWARDS INCOMPATIBILITIES:
|
||||
|
||||
* Highlighting function and method declarations/calls is fixed. To fix it we
|
||||
had to remove the meaning of the previous settings. The following setting is
|
||||
removed:
|
||||
|
||||
* `go_highlight_methods`
|
||||
|
||||
in favor of the following settings and changes:
|
||||
|
||||
* `go_highlight_functions`: This highlights now all function and method
|
||||
declarations (whereas previously it would also highlight function and
|
||||
method calls, not anymore)
|
||||
* `go_highlight_function_calls`: This higlights now all all function and
|
||||
method calls.
|
||||
[[GH-1557]](https://github.com/fatih/vim-go/pull/1557)
|
||||
* Rename g`g:go_metalinter_excludes` to `g:go_metalinter_disabled`.
|
||||
[[GH-1648]](https://github.com/fatih/vim-go/pull/1648)
|
||||
* `:GoBuild` doesn't append the `-i` flag anymore due the recent Go 1.10
|
||||
changes that introduced a build cache.
|
||||
[[GH-1701]](https://github.com/fatih/vim-go/pull/1701)
|
||||
|
||||
## 1.16 - (December 29, 2017)
|
||||
|
||||
FEATURES:
|
||||
|
||||
@ -12,13 +12,13 @@ This plugin adds Go language support for Vim, with the following main features:
|
||||
with `:GoTest`. Run a single tests with `:GoTestFunc`).
|
||||
* Quickly execute your current file(s) with `:GoRun`.
|
||||
* Improved syntax highlighting and folding.
|
||||
* Debug programs with integrated `delve` support with `:GoDebugStart`.
|
||||
* Completion support via `gocode`.
|
||||
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
|
||||
* Go to symbol/declaration with `:GoDef`.
|
||||
* Look up documentation with `:GoDoc` or `:GoDocBrowser`.
|
||||
* Easily import packages via `:GoImport`, remove them via `:GoDrop`.
|
||||
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
|
||||
display `GOPATH` with `:GoPath`.
|
||||
* Precise type-safe renaming of identifiers with `:GoRename`.
|
||||
* See which code is covered by tests with `:GoCoverage`.
|
||||
* Add or remove tags on struct fields with `:GoAddTags` and `:GoRemoveTags`.
|
||||
* Call `gometalinter` with `:GoMetaLinter` to invoke all possible linters
|
||||
@ -28,7 +28,6 @@ This plugin adds Go language support for Vim, with the following main features:
|
||||
errors, or make sure errors are checked with `:GoErrCheck`.
|
||||
* Advanced source analysis tools utilizing `guru`, such as `:GoImplements`,
|
||||
`:GoCallees`, and `:GoReferrers`.
|
||||
* Precise type-safe renaming of identifiers with `:GoRename`.
|
||||
* ... and many more! Please see [doc/vim-go.txt](doc/vim-go.txt) for more
|
||||
information.
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 747 KiB |
@ -1,5 +1,5 @@
|
||||
function! go#cmd#autowrite() abort
|
||||
if &autowrite == 1
|
||||
if &autowrite == 1 || &autowriteall == 1
|
||||
silent! wall
|
||||
endif
|
||||
endfunction
|
||||
@ -17,7 +17,7 @@ function! go#cmd#Build(bang, ...) abort
|
||||
let args =
|
||||
\ ["build"] +
|
||||
\ map(copy(a:000), "expand(v:val)") +
|
||||
\ ["-i", ".", "errors"]
|
||||
\ [".", "errors"]
|
||||
|
||||
" Vim async.
|
||||
if go#util#has_job()
|
||||
@ -269,7 +269,7 @@ function s:cmd_job(args) abort
|
||||
" autowrite is not enabled for jobs
|
||||
call go#cmd#autowrite()
|
||||
|
||||
function! s:error_info_cb(job, exit_status, data) closure abort
|
||||
function! s:complete(job, exit_status, data) closure abort
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
\ 'type': a:args.cmd[1],
|
||||
@ -288,12 +288,13 @@ function s:cmd_job(args) abort
|
||||
call go#statusline#Update(status_dir, status)
|
||||
endfunction
|
||||
|
||||
let a:args.error_info_cb = funcref('s:error_info_cb')
|
||||
let a:args.complete = funcref('s:complete')
|
||||
let callbacks = go#job#Spawn(a:args)
|
||||
|
||||
let start_options = {
|
||||
\ 'callback': callbacks.callback,
|
||||
\ 'exit_cb': callbacks.exit_cb,
|
||||
\ 'close_cb': callbacks.close_cb,
|
||||
\ }
|
||||
|
||||
" pre start
|
||||
|
||||
30
autoload/go/cmd_test.vim
Normal file
30
autoload/go/cmd_test.vim
Normal file
@ -0,0 +1,30 @@
|
||||
func! Test_GoBuildErrors()
|
||||
try
|
||||
let l:filename = 'cmd/bad.go'
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
exe 'cd ' . l:tmp . '/src/cmd'
|
||||
|
||||
" set the compiler type so that the errorformat option will be set
|
||||
" correctly.
|
||||
compiler go
|
||||
|
||||
let expected = [{'lnum': 4, 'bufnr': bufnr('%'), 'col': 2, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'undefined: notafunc'}]
|
||||
" clear the quickfix lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
call go#cmd#Build(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, l:expected)
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
@ -1,58 +1,51 @@
|
||||
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
|
||||
|
||||
function! s:gocodeCurrentBuffer() abort
|
||||
let file = tempname()
|
||||
call writefile(go#util#GetLines(), file)
|
||||
return file
|
||||
endfunction
|
||||
|
||||
function! s:gocodeCommand(cmd, preargs, args) abort
|
||||
for i in range(0, len(a:args) - 1)
|
||||
let a:args[i] = go#util#Shellescape(a:args[i])
|
||||
endfor
|
||||
for i in range(0, len(a:preargs) - 1)
|
||||
let a:preargs[i] = go#util#Shellescape(a:preargs[i])
|
||||
endfor
|
||||
|
||||
function! s:gocodeCommand(cmd, args) abort
|
||||
let bin_path = go#path#CheckBinPath("gocode")
|
||||
if empty(bin_path)
|
||||
return
|
||||
return []
|
||||
endif
|
||||
|
||||
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
|
||||
|
||||
let cmd = [bin_path]
|
||||
let cmd = extend(cmd, ['-sock', socket_type])
|
||||
let cmd = extend(cmd, ['-f', 'vim'])
|
||||
let cmd = extend(cmd, [a:cmd])
|
||||
let cmd = extend(cmd, a:args)
|
||||
|
||||
return cmd
|
||||
endfunction
|
||||
|
||||
function! s:sync_gocode(cmd, args, input) abort
|
||||
" We might hit cache problems, as gocode doesn't handle different GOPATHs
|
||||
" well. See: https://github.com/nsf/gocode/issues/239
|
||||
let old_goroot = $GOROOT
|
||||
let $GOROOT = go#util#env("goroot")
|
||||
|
||||
try
|
||||
let socket_type = get(g:, 'go_gocode_socket_type', s:sock_type)
|
||||
let cmd = printf('%s -sock %s %s %s %s',
|
||||
\ go#util#Shellescape(bin_path),
|
||||
\ socket_type,
|
||||
\ join(a:preargs),
|
||||
\ go#util#Shellescape(a:cmd),
|
||||
\ join(a:args)
|
||||
\ )
|
||||
let cmd = s:gocodeCommand(a:cmd, a:args)
|
||||
" gocode can sometimes be slow, so redraw now to avoid waiting for gocode
|
||||
" to return before redrawing automatically.
|
||||
redraw
|
||||
|
||||
let result = go#util#System(cmd)
|
||||
let [l:result, l:err] = go#util#Exec(cmd, a:input)
|
||||
finally
|
||||
let $GOROOT = old_goroot
|
||||
endtry
|
||||
|
||||
if go#util#ShellError() != 0
|
||||
return "[\"0\", []]"
|
||||
else
|
||||
if &encoding != 'utf-8'
|
||||
let result = iconv(result, 'utf-8', &encoding)
|
||||
endif
|
||||
return result
|
||||
if l:err != 0
|
||||
return "[0, []]"
|
||||
endif
|
||||
|
||||
if &encoding != 'utf-8'
|
||||
let l:result = iconv(l:result, 'utf-8', &encoding)
|
||||
endif
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! s:gocodeCurrentBufferOpt(filename) abort
|
||||
return '-in=' . a:filename
|
||||
endfunction
|
||||
|
||||
" TODO(bc): reset when gocode isn't running
|
||||
let s:optionsEnabled = 0
|
||||
function! s:gocodeEnableOptions() abort
|
||||
if s:optionsEnabled
|
||||
@ -78,62 +71,161 @@ endfunction
|
||||
function! s:gocodeAutocomplete() abort
|
||||
call s:gocodeEnableOptions()
|
||||
|
||||
let filename = s:gocodeCurrentBuffer()
|
||||
let result = s:gocodeCommand('autocomplete',
|
||||
\ [s:gocodeCurrentBufferOpt(filename), '-f=vim'],
|
||||
\ [expand('%:p'), go#util#OffsetCursor()])
|
||||
call delete(filename)
|
||||
return result
|
||||
" use the offset as is, because the cursor position is the position for
|
||||
" which autocomplete candidates are needed.
|
||||
return s:sync_gocode('autocomplete',
|
||||
\ [expand('%:p'), go#util#OffsetCursor()],
|
||||
\ go#util#GetLines())
|
||||
endfunction
|
||||
|
||||
" go#complete#GoInfo returns the description of the identifier under the
|
||||
" cursor.
|
||||
function! go#complete#GetInfo() abort
|
||||
let offset = go#util#OffsetCursor()+1
|
||||
let filename = s:gocodeCurrentBuffer()
|
||||
let result = s:gocodeCommand('autocomplete',
|
||||
\ [s:gocodeCurrentBufferOpt(filename), '-f=godit'],
|
||||
\ [expand('%:p'), offset])
|
||||
call delete(filename)
|
||||
|
||||
" first line is: Charcount,,NumberOfCandidates, i.e: 8,,1
|
||||
" following lines are candiates, i.e: func foo(name string),,foo(
|
||||
let out = split(result, '\n')
|
||||
|
||||
" no candidates are found
|
||||
if len(out) == 1
|
||||
return ""
|
||||
endif
|
||||
|
||||
" only one candidate is found
|
||||
if len(out) == 2
|
||||
return split(out[1], ',,')[0]
|
||||
endif
|
||||
|
||||
" to many candidates are available, pick one that maches the word under the
|
||||
" cursor
|
||||
let infos = []
|
||||
for info in out[1:]
|
||||
call add(infos, split(info, ',,')[0])
|
||||
endfor
|
||||
|
||||
let wordMatch = '\<' . expand("<cword>") . '\>'
|
||||
" escape single quotes in wordMatch before passing it to filter
|
||||
let wordMatch = substitute(wordMatch, "'", "''", "g")
|
||||
let filtered = filter(infos, "v:val =~ '".wordMatch."'")
|
||||
|
||||
if len(filtered) == 1
|
||||
return filtered[0]
|
||||
endif
|
||||
|
||||
return ""
|
||||
return s:sync_info(0)
|
||||
endfunction
|
||||
|
||||
function! go#complete#Info(auto) abort
|
||||
if go#util#has_job()
|
||||
return s:async_info(a:auto)
|
||||
else
|
||||
return s:sync_info(a:auto)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:async_info(auto)
|
||||
if exists("s:async_info_job")
|
||||
call job_stop(s:async_info_job)
|
||||
unlet s:async_info_job
|
||||
endif
|
||||
|
||||
let state = {
|
||||
\ 'exited': 0,
|
||||
\ 'exit_status': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'messages': [],
|
||||
\ 'auto': a:auto
|
||||
\ }
|
||||
|
||||
function! s:callback(chan, msg) dict
|
||||
let l:msg = a:msg
|
||||
if &encoding != 'utf-8'
|
||||
let l:msg = iconv(l:msg, 'utf-8', &encoding)
|
||||
endif
|
||||
call add(self.messages, l:msg)
|
||||
endfunction
|
||||
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exit_status = a:exitval
|
||||
let self.exited = 1
|
||||
|
||||
if self.closed
|
||||
call self.complete()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
if self.exited
|
||||
call self.complete()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function state.complete() dict
|
||||
if self.exit_status != 0
|
||||
return
|
||||
endif
|
||||
|
||||
let result = s:info_filter(self.auto, join(self.messages, "\n"))
|
||||
call s:info_complete(self.auto, result)
|
||||
endfunction
|
||||
|
||||
" add 1 to the offset, so that the position at the cursor will be included
|
||||
" in gocode's search
|
||||
let offset = go#util#OffsetCursor()+1
|
||||
|
||||
" We might hit cache problems, as gocode doesn't handle different GOPATHs
|
||||
" well. See: https://github.com/nsf/gocode/issues/239
|
||||
let env = {
|
||||
\ "GOROOT": go#util#env("goroot")
|
||||
\ }
|
||||
|
||||
let cmd = s:gocodeCommand('autocomplete',
|
||||
\ [expand('%:p'), offset])
|
||||
|
||||
" TODO(bc): Don't write the buffer to a file; pass the buffer directrly to
|
||||
" gocode's stdin. It shouldn't be necessary to use {in_io: 'file', in_name:
|
||||
" s:gocodeFile()}, but unfortunately {in_io: 'buffer', in_buf: bufnr('%')}
|
||||
" should work.
|
||||
let options = {
|
||||
\ 'env': env,
|
||||
\ 'in_io': 'file',
|
||||
\ 'in_name': s:gocodeFile(),
|
||||
\ 'callback': funcref("s:callback", [], state),
|
||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
||||
\ 'close_cb': funcref("s:close_cb", [], state)
|
||||
\ }
|
||||
|
||||
let s:async_info_job = job_start(cmd, options)
|
||||
endfunction
|
||||
|
||||
function! s:gocodeFile()
|
||||
let file = tempname()
|
||||
call writefile(go#util#GetLines(), file)
|
||||
return file
|
||||
endfunction
|
||||
|
||||
function! s:sync_info(auto)
|
||||
" auto is true if we were called by g:go_auto_type_info's autocmd
|
||||
let result = go#complete#GetInfo()
|
||||
if !empty(result)
|
||||
" if auto, and the result is a PANIC by gocode, hide it
|
||||
if a:auto && result ==# 'PANIC PANIC PANIC' | return | endif
|
||||
echo "vim-go: " | echohl Function | echon result | echohl None
|
||||
|
||||
" add 1 to the offset, so that the position at the cursor will be included
|
||||
" in gocode's search
|
||||
let offset = go#util#OffsetCursor()+1
|
||||
|
||||
let result = s:sync_gocode('autocomplete',
|
||||
\ [expand('%:p'), offset],
|
||||
\ go#util#GetLines())
|
||||
|
||||
let result = s:info_filter(a:auto, result)
|
||||
call s:info_complete(a:auto, result)
|
||||
endfunction
|
||||
|
||||
function! s:info_filter(auto, result) abort
|
||||
if empty(a:result)
|
||||
return ""
|
||||
endif
|
||||
|
||||
let l:result = eval(a:result)
|
||||
if len(l:result) != 2
|
||||
return ""
|
||||
endif
|
||||
|
||||
let l:candidates = l:result[1]
|
||||
if len(l:candidates) == 1
|
||||
" When gocode panics in vim mode, it returns
|
||||
" [0, [{'word': 'PANIC', 'abbr': 'PANIC PANIC PANIC', 'info': 'PANIC PANIC PANIC'}]]
|
||||
if a:auto && l:candidates[0].info ==# "PANIC PANIC PANIC"
|
||||
return ""
|
||||
endif
|
||||
|
||||
return l:candidates[0].info
|
||||
endif
|
||||
|
||||
let filtered = []
|
||||
let wordMatch = '\<' . expand("<cword>") . '\>'
|
||||
" escape single quotes in wordMatch before passing it to filter
|
||||
let wordMatch = substitute(wordMatch, "'", "''", "g")
|
||||
let filtered = filter(l:candidates, "v:val.info =~ '".wordMatch."'")
|
||||
|
||||
if len(l:filtered) != 1
|
||||
return ""
|
||||
endif
|
||||
|
||||
return l:filtered[0].info
|
||||
endfunction
|
||||
|
||||
function! s:info_complete(auto, result) abort
|
||||
if !empty(a:result)
|
||||
echo "vim-go: " | echohl Function | echon a:result | echohl None
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@ -142,20 +234,22 @@ function! s:trim_bracket(val) abort
|
||||
return a:val
|
||||
endfunction
|
||||
|
||||
let s:completions = ""
|
||||
function! go#complete#Complete(findstart, base) abort
|
||||
"findstart = 1 when we need to get the text length
|
||||
if a:findstart == 1
|
||||
execute "silent let g:gocomplete_completions = " . s:gocodeAutocomplete()
|
||||
return col('.') - g:gocomplete_completions[0] - 1
|
||||
execute "silent let s:completions = " . s:gocodeAutocomplete()
|
||||
return col('.') - s:completions[0] - 1
|
||||
"findstart = 0 when we need to return the list of completions
|
||||
else
|
||||
let s = getline(".")[col('.') - 1]
|
||||
if s =~ '[(){}\{\}]'
|
||||
return map(copy(g:gocomplete_completions[1]), 's:trim_bracket(v:val)')
|
||||
return map(copy(s:completions[1]), 's:trim_bracket(v:val)')
|
||||
endif
|
||||
return g:gocomplete_completions[1]
|
||||
|
||||
return s:completions[1]
|
||||
endif
|
||||
endf
|
||||
endfunction
|
||||
|
||||
function! go#complete#ToggleAutoTypeInfo() abort
|
||||
if get(g:, "go_auto_type_info", 0)
|
||||
@ -168,5 +262,4 @@ function! go#complete#ToggleAutoTypeInfo() abort
|
||||
call go#util#EchoProgress("auto type info enabled")
|
||||
endfunction
|
||||
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
@ -45,13 +45,13 @@ function! go#coverage#Buffer(bang, ...) abort
|
||||
let l:tmpname = tempname()
|
||||
|
||||
if get(g:, 'go_echo_command_info', 1)
|
||||
echon "vim-go: " | echohl Identifier | echon "testing ..." | echohl None
|
||||
call go#util#EchoProgress("testing...")
|
||||
endif
|
||||
|
||||
if go#util#has_job()
|
||||
call s:coverage_job({
|
||||
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname] + a:000,
|
||||
\ 'custom_cb': function('s:coverage_callback', [l:tmpname]),
|
||||
\ 'complete': function('s:coverage_callback', [l:tmpname]),
|
||||
\ 'bang': a:bang,
|
||||
\ 'for': 'GoTest',
|
||||
\ })
|
||||
@ -107,7 +107,7 @@ function! go#coverage#Browser(bang, ...) abort
|
||||
if go#util#has_job()
|
||||
call s:coverage_job({
|
||||
\ 'cmd': ['go', 'test', '-coverprofile', l:tmpname],
|
||||
\ 'custom_cb': function('s:coverage_browser_callback', [l:tmpname]),
|
||||
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
|
||||
\ 'bang': a:bang,
|
||||
\ 'for': 'GoTest',
|
||||
\ })
|
||||
@ -278,7 +278,8 @@ function s:coverage_job(args)
|
||||
call go#cmd#autowrite()
|
||||
|
||||
let status_dir = expand('%:p:h')
|
||||
function! s:error_info_cb(job, exit_status, data) closure
|
||||
let Complete = a:args.complete
|
||||
function! s:complete(job, exit_status, data) closure
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
\ 'type': "coverage",
|
||||
@ -290,14 +291,16 @@ function s:coverage_job(args)
|
||||
endif
|
||||
|
||||
call go#statusline#Update(status_dir, status)
|
||||
return Complete(a:job, a:exit_status, a:data)
|
||||
endfunction
|
||||
|
||||
let a:args.error_info_cb = funcref('s:error_info_cb')
|
||||
let a:args.complete = funcref('s:complete')
|
||||
let callbacks = go#job#Spawn(a:args)
|
||||
|
||||
let start_options = {
|
||||
\ 'callback': callbacks.callback,
|
||||
\ 'exit_cb': callbacks.exit_cb,
|
||||
\ 'close_cb': callbacks.close_cb,
|
||||
\ }
|
||||
|
||||
" pre start
|
||||
|
||||
904
autoload/go/debug.vim
Normal file
904
autoload/go/debug.vim
Normal file
@ -0,0 +1,904 @@
|
||||
scriptencoding utf-8
|
||||
|
||||
if !exists('g:go_debug_windows')
|
||||
let g:go_debug_windows = {
|
||||
\ 'stack': 'leftabove 20vnew',
|
||||
\ 'out': 'botright 10new',
|
||||
\ 'vars': 'leftabove 30vnew',
|
||||
\ }
|
||||
endif
|
||||
|
||||
if !exists('g:go_debug_address')
|
||||
let g:go_debug_address = '127.0.0.1:8181'
|
||||
endif
|
||||
|
||||
if !exists('s:state')
|
||||
let s:state = {
|
||||
\ 'rpcid': 1,
|
||||
\ 'running': 0,
|
||||
\ 'breakpoint': {},
|
||||
\ 'currentThread': {},
|
||||
\ 'localVars': {},
|
||||
\ 'functionArgs': {},
|
||||
\ 'message': [],
|
||||
\ 'is_test': 0,
|
||||
\}
|
||||
|
||||
if go#util#HasDebug('debugger-state')
|
||||
let g:go_debug_diag = s:state
|
||||
endif
|
||||
endif
|
||||
|
||||
if !exists('s:start_args')
|
||||
let s:start_args = []
|
||||
endif
|
||||
|
||||
function! s:groutineID() abort
|
||||
return s:state['currentThread'].goroutineID
|
||||
endfunction
|
||||
|
||||
function! s:exit(job, status) abort
|
||||
if has_key(s:state, 'job')
|
||||
call remove(s:state, 'job')
|
||||
endif
|
||||
call s:clearState()
|
||||
if a:status > 0
|
||||
call go#util#EchoError(s:state['message'])
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:logger(prefix, ch, msg) abort
|
||||
let l:cur_win = bufwinnr('')
|
||||
let l:log_win = bufwinnr(bufnr('__GODEBUG_OUTPUT__'))
|
||||
if l:log_win == -1
|
||||
return
|
||||
endif
|
||||
exe l:log_win 'wincmd w'
|
||||
|
||||
try
|
||||
setlocal modifiable
|
||||
if getline(1) == ''
|
||||
call setline('$', a:prefix . a:msg)
|
||||
else
|
||||
call append('$', a:prefix . a:msg)
|
||||
endif
|
||||
normal! G
|
||||
setlocal nomodifiable
|
||||
finally
|
||||
exe l:cur_win 'wincmd w'
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:call_jsonrpc(method, ...) abort
|
||||
if go#util#HasDebug('debugger-commands')
|
||||
if !exists('g:go_debug_commands')
|
||||
let g:go_debug_commands = []
|
||||
endif
|
||||
echom 'sending to dlv ' . a:method
|
||||
endif
|
||||
|
||||
if len(a:000) > 0 && type(a:000[0]) == v:t_func
|
||||
let Cb = a:000[0]
|
||||
let args = a:000[1:]
|
||||
else
|
||||
let Cb = v:none
|
||||
let args = a:000
|
||||
endif
|
||||
let s:state['rpcid'] += 1
|
||||
let req_json = json_encode({
|
||||
\ 'id': s:state['rpcid'],
|
||||
\ 'method': a:method,
|
||||
\ 'params': args,
|
||||
\})
|
||||
|
||||
try
|
||||
" Use callback
|
||||
if type(Cb) == v:t_func
|
||||
let s:ch = ch_open('127.0.0.1:8181', {'mode': 'nl', 'callback': Cb})
|
||||
call ch_sendraw(s:ch, req_json)
|
||||
|
||||
if go#util#HasDebug('debugger-commands')
|
||||
let g:go_debug_commands = add(g:go_debug_commands, {
|
||||
\ 'request': req_json,
|
||||
\ 'response': Cb,
|
||||
\ })
|
||||
endif
|
||||
return
|
||||
endif
|
||||
|
||||
let ch = ch_open('127.0.0.1:8181', {'mode': 'nl', 'timeout': 20000})
|
||||
call ch_sendraw(ch, req_json)
|
||||
let resp_json = ch_readraw(ch)
|
||||
|
||||
if go#util#HasDebug('debugger-commands')
|
||||
let g:go_debug_commands = add(g:go_debug_commands, {
|
||||
\ 'request': req_json,
|
||||
\ 'response': resp_json,
|
||||
\ })
|
||||
endif
|
||||
|
||||
let obj = json_decode(resp_json)
|
||||
if type(obj) == v:t_dict && has_key(obj, 'error') && !empty(obj.error)
|
||||
throw obj.error
|
||||
endif
|
||||
return obj
|
||||
catch
|
||||
throw substitute(v:exception, '^Vim', '', '')
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Update the location of the current breakpoint or line we're halted on based on
|
||||
" response from dlv.
|
||||
function! s:update_breakpoint(res) abort
|
||||
if type(a:res) ==# v:t_none
|
||||
return
|
||||
endif
|
||||
|
||||
let state = a:res.result.State
|
||||
if !has_key(state, 'currentThread')
|
||||
return
|
||||
endif
|
||||
|
||||
let s:state['currentThread'] = state.currentThread
|
||||
let bufs = filter(map(range(1, winnr('$')), '[v:val,bufname(winbufnr(v:val))]'), 'v:val[1]=~"\.go$"')
|
||||
if len(bufs) == 0
|
||||
return
|
||||
endif
|
||||
|
||||
exe bufs[0][0] 'wincmd w'
|
||||
let filename = state.currentThread.file
|
||||
let linenr = state.currentThread.line
|
||||
let oldfile = fnamemodify(expand('%'), ':p:gs!\\!/!')
|
||||
if oldfile != filename
|
||||
silent! exe 'edit' filename
|
||||
endif
|
||||
silent! exe 'norm!' linenr.'G'
|
||||
silent! normal! zvzz
|
||||
silent! sign unplace 9999
|
||||
silent! exe 'sign place 9999 line=' . linenr . ' name=godebugcurline file=' . filename
|
||||
endfunction
|
||||
|
||||
" Populate the stacktrace window.
|
||||
function! s:show_stacktrace(res) abort
|
||||
if !has_key(a:res, 'result')
|
||||
return
|
||||
endif
|
||||
|
||||
let l:stack_win = bufwinnr(bufnr('__GODEBUG_STACKTRACE__'))
|
||||
if l:stack_win == -1
|
||||
return
|
||||
endif
|
||||
|
||||
let l:cur_win = bufwinnr('')
|
||||
exe l:stack_win 'wincmd w'
|
||||
|
||||
try
|
||||
setlocal modifiable
|
||||
silent %delete _
|
||||
for i in range(len(a:res.result.Locations))
|
||||
let loc = a:res.result.Locations[i]
|
||||
call setline(i+1, printf('%s - %s:%d', loc.function.name, fnamemodify(loc.file, ':p'), loc.line))
|
||||
endfor
|
||||
finally
|
||||
setlocal nomodifiable
|
||||
exe l:cur_win 'wincmd w'
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Populate the variable window.
|
||||
function! s:show_variables() abort
|
||||
let l:var_win = bufwinnr(bufnr('__GODEBUG_VARIABLES__'))
|
||||
if l:var_win == -1
|
||||
return
|
||||
endif
|
||||
|
||||
let l:cur_win = bufwinnr('')
|
||||
exe l:var_win 'wincmd w'
|
||||
|
||||
try
|
||||
setlocal modifiable
|
||||
silent %delete _
|
||||
|
||||
let v = []
|
||||
let v += ['# Local Variables']
|
||||
if type(get(s:state, 'localVars', [])) is type([])
|
||||
for c in s:state['localVars']
|
||||
let v += split(s:eval_tree(c, 0), "\n")
|
||||
endfor
|
||||
endif
|
||||
|
||||
let v += ['']
|
||||
let v += ['# Function Arguments']
|
||||
if type(get(s:state, 'functionArgs', [])) is type([])
|
||||
for c in s:state['functionArgs']
|
||||
let v += split(s:eval_tree(c, 0), "\n")
|
||||
endfor
|
||||
endif
|
||||
|
||||
call setline(1, v)
|
||||
finally
|
||||
setlocal nomodifiable
|
||||
exe l:cur_win 'wincmd w'
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:clearState() abort
|
||||
let s:state['currentThread'] = {}
|
||||
let s:state['localVars'] = {}
|
||||
let s:state['functionArgs'] = {}
|
||||
let s:state['message'] = []
|
||||
silent! sign unplace 9999
|
||||
endfunction
|
||||
|
||||
function! s:stop() abort
|
||||
call s:clearState()
|
||||
if has_key(s:state, 'job')
|
||||
call job_stop(s:state['job'])
|
||||
call remove(s:state, 'job')
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! go#debug#Stop() abort
|
||||
" Remove signs.
|
||||
for k in keys(s:state['breakpoint'])
|
||||
let bt = s:state['breakpoint'][k]
|
||||
if bt.id >= 0
|
||||
silent exe 'sign unplace ' . bt.id
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Remove all commands and add back the default commands.
|
||||
for k in map(split(execute('command GoDebug'), "\n")[1:], 'matchstr(v:val, "^\\s*\\zs\\S\\+")')
|
||||
exe 'delcommand' k
|
||||
endfor
|
||||
command! -nargs=* -complete=customlist,go#package#Complete GoDebugStart call go#debug#Start(0, <f-args>)
|
||||
command! -nargs=* -complete=customlist,go#package#Complete GoDebugTest call go#debug#Start(1, <f-args>)
|
||||
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
|
||||
|
||||
" Remove all mappings.
|
||||
for k in map(split(execute('map <Plug>(go-debug-'), "\n")[1:], 'matchstr(v:val, "^n\\s\\+\\zs\\S\\+")')
|
||||
exe 'unmap' k
|
||||
endfor
|
||||
|
||||
call s:stop()
|
||||
|
||||
let bufs = filter(map(range(1, winnr('$')), '[v:val,bufname(winbufnr(v:val))]'), 'v:val[1]=~"\.go$"')
|
||||
if len(bufs) > 0
|
||||
exe bufs[0][0] 'wincmd w'
|
||||
else
|
||||
wincmd p
|
||||
endif
|
||||
silent! exe bufwinnr(bufnr('__GODEBUG_STACKTRACE__')) 'wincmd c'
|
||||
silent! exe bufwinnr(bufnr('__GODEBUG_VARIABLES__')) 'wincmd c'
|
||||
silent! exe bufwinnr(bufnr('__GODEBUG_OUTPUT__')) 'wincmd c'
|
||||
|
||||
set noballooneval
|
||||
set balloonexpr=
|
||||
endfunction
|
||||
|
||||
function! s:goto_file() abort
|
||||
let m = matchlist(getline('.'), ' - \(.*\):\([0-9]\+\)$')
|
||||
if m[1] == ''
|
||||
return
|
||||
endif
|
||||
let bufs = filter(map(range(1, winnr('$')), '[v:val,bufname(winbufnr(v:val))]'), 'v:val[1]=~"\.go$"')
|
||||
if len(bufs) == 0
|
||||
return
|
||||
endif
|
||||
exe bufs[0][0] 'wincmd w'
|
||||
let filename = m[1]
|
||||
let linenr = m[2]
|
||||
let oldfile = fnamemodify(expand('%'), ':p:gs!\\!/!')
|
||||
if oldfile != filename
|
||||
silent! exe 'edit' filename
|
||||
endif
|
||||
silent! exe 'norm!' linenr.'G'
|
||||
silent! normal! zvzz
|
||||
endfunction
|
||||
|
||||
function! s:delete_expands()
|
||||
let nr = line('.')
|
||||
while 1
|
||||
let l = getline(nr+1)
|
||||
if empty(l) || l =~ '^\S'
|
||||
return
|
||||
endif
|
||||
silent! exe (nr+1) . 'd _'
|
||||
endwhile
|
||||
silent! exe 'norm!' nr.'G'
|
||||
endfunction
|
||||
|
||||
function! s:expand_var() abort
|
||||
" Get name from struct line.
|
||||
let name = matchstr(getline('.'), '^[^:]\+\ze: [a-zA-Z0-9\.·]\+{\.\.\.}$')
|
||||
" Anonymous struct
|
||||
if name == ''
|
||||
let name = matchstr(getline('.'), '^[^:]\+\ze: struct {.\{-}}$')
|
||||
endif
|
||||
|
||||
if name != ''
|
||||
setlocal modifiable
|
||||
let not_open = getline(line('.')+1) !~ '^ '
|
||||
let l = line('.')
|
||||
call s:delete_expands()
|
||||
|
||||
if not_open
|
||||
call append(l, split(s:eval(name), "\n")[1:])
|
||||
endif
|
||||
silent! exe 'norm!' l.'G'
|
||||
setlocal nomodifiable
|
||||
return
|
||||
endif
|
||||
|
||||
" Expand maps
|
||||
let m = matchlist(getline('.'), '^[^:]\+\ze: map.\{-}\[\(\d\+\)\]$')
|
||||
if len(m) > 0 && m[1] != ''
|
||||
setlocal modifiable
|
||||
let not_open = getline(line('.')+1) !~ '^ '
|
||||
let l = line('.')
|
||||
call s:delete_expands()
|
||||
if not_open
|
||||
" TODO: Not sure how to do this yet... Need to get keys of the map.
|
||||
" let vs = ''
|
||||
" for i in range(0, min([10, m[1]-1]))
|
||||
" let vs .= ' ' . s:eval(printf("%s[%s]", m[0], ))
|
||||
" endfor
|
||||
" call append(l, split(vs, "\n"))
|
||||
endif
|
||||
|
||||
silent! exe 'norm!' l.'G'
|
||||
setlocal nomodifiable
|
||||
return
|
||||
endif
|
||||
|
||||
" Expand string.
|
||||
let m = matchlist(getline('.'), '^\([^:]\+\)\ze: \(string\)\[\([0-9]\+\)\]\(: .\{-}\)\?$')
|
||||
if len(m) > 0 && m[1] != ''
|
||||
setlocal modifiable
|
||||
let not_open = getline(line('.')+1) !~ '^ '
|
||||
let l = line('.')
|
||||
call s:delete_expands()
|
||||
|
||||
if not_open
|
||||
let vs = ''
|
||||
for i in range(0, min([10, m[3]-1]))
|
||||
let vs .= ' ' . s:eval(m[1] . '[' . i . ']')
|
||||
endfor
|
||||
call append(l, split(vs, "\n"))
|
||||
endif
|
||||
|
||||
silent! exe 'norm!' l.'G'
|
||||
setlocal nomodifiable
|
||||
return
|
||||
endif
|
||||
|
||||
" Expand slice.
|
||||
let m = matchlist(getline('.'), '^\([^:]\+\)\ze: \(\[\]\w\{-}\)\[\([0-9]\+\)\]$')
|
||||
if len(m) > 0 && m[1] != ''
|
||||
setlocal modifiable
|
||||
let not_open = getline(line('.')+1) !~ '^ '
|
||||
let l = line('.')
|
||||
call s:delete_expands()
|
||||
|
||||
if not_open
|
||||
let vs = ''
|
||||
for i in range(0, min([10, m[3]-1]))
|
||||
let vs .= ' ' . s:eval(m[1] . '[' . i . ']')
|
||||
endfor
|
||||
call append(l, split(vs, "\n"))
|
||||
endif
|
||||
silent! exe 'norm!' l.'G'
|
||||
setlocal nomodifiable
|
||||
return
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:start_cb(ch, json) abort
|
||||
let res = json_decode(a:json)
|
||||
if type(res) == v:t_dict && has_key(res, 'error') && !empty(res.error)
|
||||
throw res.error
|
||||
endif
|
||||
if empty(res) || !has_key(res, 'result')
|
||||
return
|
||||
endif
|
||||
for bt in res.result.Breakpoints
|
||||
if bt.id >= 0
|
||||
let s:state['breakpoint'][bt.id] = bt
|
||||
exe 'sign place '. bt.id .' line=' . bt.line . ' name=godebugbreakpoint file=' . bt.file
|
||||
endif
|
||||
endfor
|
||||
|
||||
let oldbuf = bufnr('%')
|
||||
silent! only!
|
||||
|
||||
let winnum = bufwinnr(bufnr('__GODEBUG_STACKTRACE__'))
|
||||
if winnum != -1
|
||||
return
|
||||
endif
|
||||
|
||||
if exists('g:go_debug_windows["stack"]') && g:go_debug_windows['stack'] != ''
|
||||
exe 'silent ' . g:go_debug_windows['stack']
|
||||
silent file `='__GODEBUG_STACKTRACE__'`
|
||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||
setlocal filetype=godebugstacktrace
|
||||
nmap <buffer> <cr> :<c-u>call <SID>goto_file()<cr>
|
||||
nmap <buffer> q <Plug>(go-debug-stop)
|
||||
endif
|
||||
|
||||
if exists('g:go_debug_windows["out"]') && g:go_debug_windows['out'] != ''
|
||||
exe 'silent ' . g:go_debug_windows['out']
|
||||
silent file `='__GODEBUG_OUTPUT__'`
|
||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||
setlocal filetype=godebugoutput
|
||||
nmap <buffer> q <Plug>(go-debug-stop)
|
||||
endif
|
||||
|
||||
if exists('g:go_debug_windows["vars"]') && g:go_debug_windows['vars'] != ''
|
||||
exe 'silent ' . g:go_debug_windows['vars']
|
||||
silent file `='__GODEBUG_VARIABLES__'`
|
||||
setlocal buftype=nofile bufhidden=wipe nomodified nobuflisted noswapfile nowrap nonumber nocursorline
|
||||
setlocal filetype=godebugvariables
|
||||
call append(0, ["# Local Variables", "", "# Function Arguments"])
|
||||
nmap <buffer> <silent> <cr> :<c-u>call <SID>expand_var()<cr>
|
||||
nmap <buffer> q <Plug>(go-debug-stop)
|
||||
endif
|
||||
|
||||
silent! delcommand GoDebugStart
|
||||
silent! delcommand GoDebugTest
|
||||
command! -nargs=0 GoDebugContinue call go#debug#Stack('continue')
|
||||
command! -nargs=0 GoDebugNext call go#debug#Stack('next')
|
||||
command! -nargs=0 GoDebugStep call go#debug#Stack('step')
|
||||
command! -nargs=0 GoDebugStepOut call go#debug#Stack('stepOut')
|
||||
command! -nargs=0 GoDebugRestart call go#debug#Restart()
|
||||
command! -nargs=0 GoDebugStop call go#debug#Stop()
|
||||
command! -nargs=* GoDebugSet call go#debug#Set(<f-args>)
|
||||
command! -nargs=1 GoDebugPrint call go#debug#Print(<q-args>)
|
||||
|
||||
nnoremap <silent> <Plug>(go-debug-breakpoint) :<C-u>call go#debug#Breakpoint()<CR>
|
||||
nnoremap <silent> <Plug>(go-debug-next) :<C-u>call go#debug#Stack('next')<CR>
|
||||
nnoremap <silent> <Plug>(go-debug-step) :<C-u>call go#debug#Stack('step')<CR>
|
||||
nnoremap <silent> <Plug>(go-debug-stepout) :<C-u>call go#debug#Stack('stepout')<CR>
|
||||
nnoremap <silent> <Plug>(go-debug-continue) :<C-u>call go#debug#Stack('continue')<CR>
|
||||
nnoremap <silent> <Plug>(go-debug-stop) :<C-u>call go#debug#Stop()<CR>
|
||||
nnoremap <silent> <Plug>(go-debug-print) :<C-u>call go#debug#Print(expand('<cword>'))<CR>
|
||||
|
||||
nmap <F5> <Plug>(go-debug-continue)
|
||||
nmap <F6> <Plug>(go-debug-print)
|
||||
nmap <F9> <Plug>(go-debug-breakpoint)
|
||||
nmap <F10> <Plug>(go-debug-next)
|
||||
nmap <F11> <Plug>(go-debug-step)
|
||||
|
||||
set balloonexpr=go#debug#BalloonExpr()
|
||||
set ballooneval
|
||||
|
||||
exe bufwinnr(oldbuf) 'wincmd w'
|
||||
endfunction
|
||||
|
||||
function! s:err_cb(ch, msg) abort
|
||||
call go#util#EchoError(a:msg)
|
||||
let s:state['message'] += [a:msg]
|
||||
endfunction
|
||||
|
||||
function! s:out_cb(ch, msg) abort
|
||||
call go#util#EchoProgress(a:msg)
|
||||
let s:state['message'] += [a:msg]
|
||||
|
||||
" TODO: why do this in this callback?
|
||||
if stridx(a:msg, g:go_debug_address) != -1
|
||||
call ch_setoptions(a:ch, {
|
||||
\ 'out_cb': function('s:logger', ['OUT: ']),
|
||||
\ 'err_cb': function('s:logger', ['ERR: ']),
|
||||
\})
|
||||
|
||||
" Tell dlv about the breakpoints that the user added before delve started.
|
||||
let l:breaks = copy(s:state.breakpoint)
|
||||
let s:state['breakpoint'] = {}
|
||||
for l:bt in values(l:breaks)
|
||||
call go#debug#Breakpoint(bt.line)
|
||||
endfor
|
||||
|
||||
call s:call_jsonrpc('RPCServer.ListBreakpoints', function('s:start_cb'))
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Start the debug mode. The first argument is the package name to compile and
|
||||
" debug, anything else will be passed to the running program.
|
||||
function! go#debug#Start(is_test, ...) abort
|
||||
if has('nvim')
|
||||
call go#util#EchoError('This feature only works in Vim for now; Neovim is not (yet) supported. Sorry :-(')
|
||||
return
|
||||
endif
|
||||
if !go#util#has_job()
|
||||
call go#util#EchoError('This feature requires Vim 8.0.0087 or newer with +job.')
|
||||
return
|
||||
endif
|
||||
|
||||
" It's already running.
|
||||
if has_key(s:state, 'job') && job_status(s:state['job']) == 'run'
|
||||
return
|
||||
endif
|
||||
|
||||
let s:start_args = a:000
|
||||
|
||||
if go#util#HasDebug('debugger-state')
|
||||
let g:go_debug_diag = s:state
|
||||
endif
|
||||
|
||||
" cd in to test directory; this is also what running "go test" does.
|
||||
if a:is_test
|
||||
lcd %:p:h
|
||||
endif
|
||||
|
||||
let s:state.is_test = a:is_test
|
||||
|
||||
let dlv = go#path#CheckBinPath("dlv")
|
||||
if empty(dlv)
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
if len(a:000) > 0
|
||||
let l:pkgname = a:1
|
||||
" Expand .; otherwise this won't work from a tmp dir.
|
||||
if l:pkgname[0] == '.'
|
||||
let l:pkgname = go#package#FromPath(getcwd()) . l:pkgname[1:]
|
||||
endif
|
||||
else
|
||||
let l:pkgname = go#package#FromPath(getcwd())
|
||||
endif
|
||||
|
||||
let l:args = []
|
||||
if len(a:000) > 1
|
||||
let l:args = ['--'] + a:000[1:]
|
||||
endif
|
||||
|
||||
let l:cmd = [
|
||||
\ dlv,
|
||||
\ (a:is_test ? 'test' : 'debug'),
|
||||
\ '--output', tempname(),
|
||||
\ '--headless',
|
||||
\ '--api-version', '2',
|
||||
\ '--log',
|
||||
\ '--listen', g:go_debug_address,
|
||||
\ '--accept-multiclient',
|
||||
\]
|
||||
if get(g:, 'go_build_tags', '') isnot ''
|
||||
let l:cmd += ['--build-flags', '--tags=' . g:go_build_tags]
|
||||
endif
|
||||
let l:cmd += l:args
|
||||
|
||||
call go#util#EchoProgress('Starting GoDebug...')
|
||||
let s:state['message'] = []
|
||||
let s:state['job'] = job_start(l:cmd, {
|
||||
\ 'out_cb': function('s:out_cb'),
|
||||
\ 'err_cb': function('s:err_cb'),
|
||||
\ 'exit_cb': function('s:exit'),
|
||||
\ 'stoponexit': 'kill',
|
||||
\})
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Translate a reflect kind constant to a human string.
|
||||
function! s:reflect_kind(k)
|
||||
" Kind constants from Go's reflect package.
|
||||
return [
|
||||
\ 'Invalid Kind',
|
||||
\ 'Bool',
|
||||
\ 'Int',
|
||||
\ 'Int8',
|
||||
\ 'Int16',
|
||||
\ 'Int32',
|
||||
\ 'Int64',
|
||||
\ 'Uint',
|
||||
\ 'Uint8',
|
||||
\ 'Uint16',
|
||||
\ 'Uint32',
|
||||
\ 'Uint64',
|
||||
\ 'Uintptr',
|
||||
\ 'Float32',
|
||||
\ 'Float64',
|
||||
\ 'Complex64',
|
||||
\ 'Complex128',
|
||||
\ 'Array',
|
||||
\ 'Chan',
|
||||
\ 'Func',
|
||||
\ 'Interface',
|
||||
\ 'Map',
|
||||
\ 'Ptr',
|
||||
\ 'Slice',
|
||||
\ 'String',
|
||||
\ 'Struct',
|
||||
\ 'UnsafePointer',
|
||||
\ ][a:k]
|
||||
endfunction
|
||||
|
||||
function! s:eval_tree(var, nest) abort
|
||||
if a:var.name =~ '^\~'
|
||||
return ''
|
||||
endif
|
||||
let nest = a:nest
|
||||
let v = ''
|
||||
let kind = s:reflect_kind(a:var.kind)
|
||||
if !empty(a:var.name)
|
||||
let v .= repeat(' ', nest) . a:var.name . ': '
|
||||
|
||||
if kind == 'Bool'
|
||||
let v .= printf("%s\n", a:var.value)
|
||||
|
||||
elseif kind == 'Struct'
|
||||
" Anonymous struct
|
||||
if a:var.type[:8] == 'struct { '
|
||||
let v .= printf("%s\n", a:var.type)
|
||||
else
|
||||
let v .= printf("%s{...}\n", a:var.type)
|
||||
endif
|
||||
|
||||
elseif kind == 'String'
|
||||
let v .= printf("%s[%d]%s\n", a:var.type, a:var.len,
|
||||
\ len(a:var.value) > 0 ? ': ' . a:var.value : '')
|
||||
|
||||
elseif kind == 'Slice' || kind == 'String' || kind == 'Map' || kind == 'Array'
|
||||
let v .= printf("%s[%d]\n", a:var.type, a:var.len)
|
||||
|
||||
elseif kind == 'Chan' || kind == 'Func' || kind == 'Interface'
|
||||
let v .= printf("%s\n", a:var.type)
|
||||
|
||||
elseif kind == 'Ptr'
|
||||
" TODO: We can do something more useful here.
|
||||
let v .= printf("%s\n", a:var.type)
|
||||
|
||||
elseif kind == 'Complex64' || kind == 'Complex128'
|
||||
let v .= printf("%s%s\n", a:var.type, a:var.value)
|
||||
|
||||
" Int, Float
|
||||
else
|
||||
let v .= printf("%s(%s)\n", a:var.type, a:var.value)
|
||||
endif
|
||||
else
|
||||
let nest -= 1
|
||||
endif
|
||||
|
||||
if index(['Chan', 'Complex64', 'Complex128'], kind) == -1 && a:var.type != 'error'
|
||||
for c in a:var.children
|
||||
let v .= s:eval_tree(c, nest+1)
|
||||
endfor
|
||||
endif
|
||||
return v
|
||||
endfunction
|
||||
|
||||
function! s:eval(arg) abort
|
||||
try
|
||||
let res = s:call_jsonrpc('RPCServer.State')
|
||||
let goroutineID = res.result.State.currentThread.goroutineID
|
||||
let res = s:call_jsonrpc('RPCServer.Eval', {
|
||||
\ 'expr': a:arg,
|
||||
\ 'scope': {'GoroutineID': goroutineID}
|
||||
\ })
|
||||
return s:eval_tree(res.result.Variable, 0)
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
return ''
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! go#debug#BalloonExpr() abort
|
||||
silent! let l:v = s:eval(v:beval_text)
|
||||
return l:v
|
||||
endfunction
|
||||
|
||||
function! go#debug#Print(arg) abort
|
||||
try
|
||||
echo substitute(s:eval(a:arg), "\n$", "", 0)
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:update_variables() abort
|
||||
" FollowPointers requests pointers to be automatically dereferenced.
|
||||
" MaxVariableRecurse is how far to recurse when evaluating nested types.
|
||||
" MaxStringLen is the maximum number of bytes read from a string
|
||||
" MaxArrayValues is the maximum number of elements read from an array, a slice or a map.
|
||||
" MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
|
||||
let l:cfg = {
|
||||
\ 'scope': {'GoroutineID': s:groutineID()},
|
||||
\ 'cfg': {'MaxStringLen': 20, 'MaxArrayValues': 20}
|
||||
\ }
|
||||
|
||||
try
|
||||
let res = s:call_jsonrpc('RPCServer.ListLocalVars', l:cfg)
|
||||
let s:state['localVars'] = res.result['Variables']
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
|
||||
try
|
||||
let res = s:call_jsonrpc('RPCServer.ListFunctionArgs', l:cfg)
|
||||
let s:state['functionArgs'] = res.result['Args']
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
|
||||
call s:show_variables()
|
||||
endfunction
|
||||
|
||||
function! go#debug#Set(symbol, value) abort
|
||||
try
|
||||
let res = s:call_jsonrpc('RPCServer.State')
|
||||
let goroutineID = res.result.State.currentThread.goroutineID
|
||||
call s:call_jsonrpc('RPCServer.Set', {
|
||||
\ 'symbol': a:symbol,
|
||||
\ 'value': a:value,
|
||||
\ 'scope': {'GoroutineID': goroutineID}
|
||||
\ })
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
|
||||
call s:update_variables()
|
||||
endfunction
|
||||
|
||||
function! s:update_stacktrace() abort
|
||||
try
|
||||
let res = s:call_jsonrpc('RPCServer.Stacktrace', {'id': s:groutineID(), 'depth': 5})
|
||||
call s:show_stacktrace(res)
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! s:stack_cb(ch, json) abort
|
||||
let s:stack_name = ''
|
||||
let res = json_decode(a:json)
|
||||
if type(res) == v:t_dict && has_key(res, 'error') && !empty(res.error)
|
||||
call go#util#EchoError(res.error)
|
||||
call s:clearState()
|
||||
call go#debug#Restart()
|
||||
return
|
||||
endif
|
||||
|
||||
if empty(res) || !has_key(res, 'result')
|
||||
return
|
||||
endif
|
||||
call s:update_breakpoint(res)
|
||||
call s:update_stacktrace()
|
||||
call s:update_variables()
|
||||
endfunction
|
||||
|
||||
" Send a command to change the cursor location to Delve.
|
||||
"
|
||||
" a:name must be one of continue, next, step, or stepOut.
|
||||
function! go#debug#Stack(name) abort
|
||||
let l:name = a:name
|
||||
|
||||
" Run continue if the program hasn't started yet.
|
||||
if s:state.running is 0
|
||||
let s:state.running = 1
|
||||
let l:name = 'continue'
|
||||
endif
|
||||
|
||||
" Add a breakpoint to the main.Main if the user didn't define any.
|
||||
if len(s:state['breakpoint']) is 0
|
||||
if go#debug#Breakpoint() isnot 0
|
||||
let s:state.running = 0
|
||||
return
|
||||
endif
|
||||
endif
|
||||
|
||||
try
|
||||
" TODO: document why this is needed.
|
||||
if l:name is# 'next' && get(s:, 'stack_name', '') is# 'next'
|
||||
call s:call_jsonrpc('RPCServer.CancelNext')
|
||||
endif
|
||||
let s:stack_name = l:name
|
||||
call s:call_jsonrpc('RPCServer.Command', function('s:stack_cb'), {'name': l:name})
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
function! go#debug#Restart() abort
|
||||
try
|
||||
call job_stop(s:state['job'])
|
||||
while has_key(s:state, 'job') && job_status(s:state['job']) is# 'run'
|
||||
sleep 50m
|
||||
endwhile
|
||||
|
||||
let l:breaks = s:state['breakpoint']
|
||||
let s:state = {
|
||||
\ 'rpcid': 1,
|
||||
\ 'running': 0,
|
||||
\ 'breakpoint': {},
|
||||
\ 'currentThread': {},
|
||||
\ 'localVars': {},
|
||||
\ 'functionArgs': {},
|
||||
\ 'message': [],
|
||||
\}
|
||||
|
||||
" Preserve breakpoints.
|
||||
for bt in values(l:breaks)
|
||||
" TODO: should use correct filename
|
||||
exe 'sign unplace '. bt.id .' file=' . bt.file
|
||||
call go#debug#Breakpoint(bt.line)
|
||||
endfor
|
||||
call call('go#debug#Start', s:start_args)
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
endtry
|
||||
endfunction
|
||||
|
||||
" Report if debugger mode is active.
|
||||
function! s:isActive()
|
||||
return len(s:state['message']) > 0
|
||||
endfunction
|
||||
|
||||
" Toggle breakpoint. Returns 0 on success and 1 on failure.
|
||||
function! go#debug#Breakpoint(...) abort
|
||||
let l:filename = fnamemodify(expand('%'), ':p:gs!\\!/!')
|
||||
|
||||
" Get line number from argument.
|
||||
if len(a:000) > 0
|
||||
let linenr = str2nr(a:1)
|
||||
if linenr is 0
|
||||
call go#util#EchoError('not a number: ' . a:1)
|
||||
return 0
|
||||
endif
|
||||
else
|
||||
let linenr = line('.')
|
||||
endif
|
||||
|
||||
try
|
||||
" Check if we already have a breakpoint for this line.
|
||||
let found = v:none
|
||||
for k in keys(s:state.breakpoint)
|
||||
let bt = s:state.breakpoint[k]
|
||||
if bt.file == l:filename && bt.line == linenr
|
||||
let found = bt
|
||||
break
|
||||
endif
|
||||
endfor
|
||||
|
||||
" Remove breakpoint.
|
||||
if type(found) == v:t_dict
|
||||
call remove(s:state['breakpoint'], bt.id)
|
||||
exe 'sign unplace '. found.id .' file=' . found.file
|
||||
if s:isActive()
|
||||
let res = s:call_jsonrpc('RPCServer.ClearBreakpoint', {'id': found.id})
|
||||
endif
|
||||
" Add breakpoint.
|
||||
else
|
||||
if s:isActive()
|
||||
let res = s:call_jsonrpc('RPCServer.CreateBreakpoint', {'Breakpoint': {'file': l:filename, 'line': linenr}})
|
||||
let bt = res.result.Breakpoint
|
||||
exe 'sign place '. bt.id .' line=' . bt.line . ' name=godebugbreakpoint file=' . bt.file
|
||||
let s:state['breakpoint'][bt.id] = bt
|
||||
else
|
||||
let id = len(s:state['breakpoint']) + 1
|
||||
let s:state['breakpoint'][id] = {'id': id, 'file': l:filename, 'line': linenr}
|
||||
exe 'sign place '. id .' line=' . linenr . ' name=godebugbreakpoint file=' . l:filename
|
||||
endif
|
||||
endif
|
||||
catch
|
||||
call go#util#EchoError(v:exception)
|
||||
return 1
|
||||
endtry
|
||||
|
||||
return 0
|
||||
endfunction
|
||||
|
||||
sign define godebugbreakpoint text=> texthl=GoDebugBreakpoint
|
||||
sign define godebugcurline text== linehl=GoDebugCurrent texthl=GoDebugCurrent
|
||||
|
||||
fun! s:hi()
|
||||
hi GoDebugBreakpoint term=standout ctermbg=117 ctermfg=0 guibg=#BAD4F5 guifg=Black
|
||||
hi GoDebugCurrent term=reverse ctermbg=12 ctermfg=7 guibg=DarkBlue guifg=White
|
||||
endfun
|
||||
augroup vim-go-breakpoint
|
||||
autocmd!
|
||||
autocmd ColorScheme * call s:hi()
|
||||
augroup end
|
||||
call s:hi()
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
@ -53,7 +53,7 @@ function! go#def#Jump(mode) abort
|
||||
if go#util#has_job()
|
||||
let l:spawn_args = {
|
||||
\ 'cmd': cmd,
|
||||
\ 'custom_cb': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
|
||||
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
|
||||
\ }
|
||||
|
||||
if &modified
|
||||
@ -292,16 +292,12 @@ function! go#def#Stack(...) abort
|
||||
endfunction
|
||||
|
||||
function s:def_job(args) abort
|
||||
function! s:error_info_cb(job, exit_status, data) closure
|
||||
" do not print anything during async definition search&jump
|
||||
endfunction
|
||||
|
||||
let a:args.error_info_cb = funcref('s:error_info_cb')
|
||||
let callbacks = go#job#Spawn(a:args)
|
||||
|
||||
let start_options = {
|
||||
\ 'callback': callbacks.callback,
|
||||
\ 'exit_cb': callbacks.exit_cb,
|
||||
\ 'close_cb': callbacks.close_cb,
|
||||
\ }
|
||||
|
||||
if &modified
|
||||
|
||||
@ -29,18 +29,7 @@ function! go#doc#OpenBrowser(...) abort
|
||||
let name = out["name"]
|
||||
let decl = out["decl"]
|
||||
|
||||
let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org')
|
||||
if godoc_url isnot 'https://godoc.org'
|
||||
" strip last '/' character if available
|
||||
let last_char = strlen(godoc_url) - 1
|
||||
if godoc_url[last_char] == '/'
|
||||
let godoc_url = strpart(godoc_url, 0, last_char)
|
||||
endif
|
||||
|
||||
" custom godoc installations expects it
|
||||
let godoc_url .= "/pkg"
|
||||
endif
|
||||
|
||||
let godoc_url = s:custom_godoc_url()
|
||||
let godoc_url .= "/" . import
|
||||
if decl !~ "^package"
|
||||
let godoc_url .= "#" . name
|
||||
@ -61,7 +50,7 @@ function! go#doc#OpenBrowser(...) abort
|
||||
let exported_name = pkgs[1]
|
||||
|
||||
" example url: https://godoc.org/github.com/fatih/set#Set
|
||||
let godoc_url = "https://godoc.org/" . pkg . "#" . exported_name
|
||||
let godoc_url = s:custom_godoc_url() . "/" . pkg . "#" . exported_name
|
||||
call go#tool#OpenBrowser(godoc_url)
|
||||
endfunction
|
||||
|
||||
@ -217,13 +206,18 @@ function! s:godocWord(args) abort
|
||||
return [pkg, exported_name]
|
||||
endfunction
|
||||
|
||||
function! s:godocNotFound(content) abort
|
||||
if len(a:content) == 0
|
||||
return 1
|
||||
function! s:custom_godoc_url() abort
|
||||
let godoc_url = get(g:, 'go_doc_url', 'https://godoc.org')
|
||||
if godoc_url isnot 'https://godoc.org'
|
||||
" strip last '/' character if available
|
||||
let last_char = strlen(godoc_url) - 1
|
||||
if godoc_url[last_char] == '/'
|
||||
let godoc_url = strpart(godoc_url, 0, last_char)
|
||||
endif
|
||||
" custom godoc installations expect /pkg before package names
|
||||
let godoc_url .= "/pkg"
|
||||
endif
|
||||
|
||||
return a:content =~# '^.*: no such file or directory\n$'
|
||||
return godoc_url
|
||||
endfunction
|
||||
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
@ -148,7 +148,6 @@ function! go#fmt#update_file(source, target)
|
||||
|
||||
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
@ -78,7 +78,7 @@ function! s:guru_cmd(args) range abort
|
||||
let scopes = go#util#StripTrailingSlash(scopes)
|
||||
|
||||
" create shell-safe entries of the list
|
||||
if !go#util#has_job() | let scopes = go#util#Shelllist(scopes) | endif
|
||||
if !has("nvim") && !go#util#has_job() | let scopes = go#util#Shelllist(scopes) | endif
|
||||
|
||||
" guru expect a comma-separated list of patterns, construct it
|
||||
let l:scope = join(scopes, ",")
|
||||
@ -129,7 +129,7 @@ function! s:sync_guru(args) abort
|
||||
endif
|
||||
|
||||
if has_key(a:args, 'custom_parse')
|
||||
call a:args.custom_parse(go#util#ShellError(), out)
|
||||
call a:args.custom_parse(go#util#ShellError(), out, a:args.mode)
|
||||
else
|
||||
call s:parse_guru_output(go#util#ShellError(), out, a:args.mode)
|
||||
endif
|
||||
@ -137,6 +137,33 @@ function! s:sync_guru(args) abort
|
||||
return out
|
||||
endfunc
|
||||
|
||||
" use vim or neovim job api as appropriate
|
||||
function! s:job_start(cmd, start_options) abort
|
||||
if go#util#has_job()
|
||||
return job_start(a:cmd, a:start_options)
|
||||
endif
|
||||
|
||||
let opts = {'stdout_buffered': v:true, 'stderr_buffered': v:true}
|
||||
function opts.on_stdout(job_id, data, event) closure
|
||||
call a:start_options.callback(a:job_id, join(a:data, "\n"))
|
||||
endfunction
|
||||
function opts.on_stderr(job_id, data, event) closure
|
||||
call a:start_options.callback(a:job_id, join(a:data, "\n"))
|
||||
endfunction
|
||||
function opts.on_exit(job_id, exit_code, event) closure
|
||||
call a:start_options.exit_cb(a:job_id, a:exit_code)
|
||||
call a:start_options.close_cb(a:job_id)
|
||||
endfunction
|
||||
|
||||
" use a shell for input redirection if needed
|
||||
let cmd = a:cmd
|
||||
if has_key(a:start_options, 'in_io') && a:start_options.in_io ==# 'file' && !empty(a:start_options.in_name)
|
||||
let cmd = ['/bin/sh', '-c', join(a:cmd, ' ') . ' <' . a:start_options.in_name]
|
||||
endif
|
||||
|
||||
return jobstart(cmd, opts)
|
||||
endfunction
|
||||
|
||||
" async_guru runs guru in async mode with the given arguments
|
||||
function! s:async_guru(args) abort
|
||||
let result = s:guru_cmd(a:args)
|
||||
@ -145,8 +172,6 @@ function! s:async_guru(args) abort
|
||||
return
|
||||
endif
|
||||
|
||||
let status_dir = expand('%:p:h')
|
||||
let statusline_type = printf("%s", a:args.mode)
|
||||
|
||||
if !has_key(a:args, 'disable_progress')
|
||||
if a:args.needs_scope
|
||||
@ -155,44 +180,64 @@ function! s:async_guru(args) abort
|
||||
endif
|
||||
endif
|
||||
|
||||
let messages = []
|
||||
function! s:callback(chan, msg) closure
|
||||
call add(messages, a:msg)
|
||||
let state = {
|
||||
\ 'status_dir': expand('%:p:h'),
|
||||
\ 'statusline_type': printf("%s", a:args.mode),
|
||||
\ 'mode': a:args.mode,
|
||||
\ 'status': {},
|
||||
\ 'exitval': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'exited': 0,
|
||||
\ 'messages': [],
|
||||
\ 'parse' : get(a:args, 'custom_parse', funcref("s:parse_guru_output"))
|
||||
\ }
|
||||
|
||||
function! s:callback(chan, msg) dict
|
||||
call add(self.messages, a:msg)
|
||||
endfunction
|
||||
|
||||
let status = {}
|
||||
let exitval = 0
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
|
||||
function! s:exit_cb(job, exitval) closure
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
\ 'type': statusline_type,
|
||||
\ 'type': self.statusline_type,
|
||||
\ 'state': "finished",
|
||||
\ }
|
||||
|
||||
if a:exitval
|
||||
let exitval = a:exitval
|
||||
let self.exitval = a:exitval
|
||||
let status.state = "failed"
|
||||
endif
|
||||
|
||||
call go#statusline#Update(status_dir, status)
|
||||
endfunction
|
||||
call go#statusline#Update(self.status_dir, status)
|
||||
|
||||
function! s:close_cb(ch) closure
|
||||
let out = join(messages, "\n")
|
||||
|
||||
if has_key(a:args, 'custom_parse')
|
||||
call a:args.custom_parse(exitval, out)
|
||||
else
|
||||
call s:parse_guru_output(exitval, out, a:args.mode)
|
||||
if self.closed
|
||||
call self.complete()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if self.exited
|
||||
call self.complete()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function state.complete() dict
|
||||
let out = join(self.messages, "\n")
|
||||
|
||||
call self.parse(self.exitval, out, self.mode)
|
||||
endfunction
|
||||
|
||||
" explicitly bind the callbacks to state so that self within them always
|
||||
" refers to state. See :help Partial for more information.
|
||||
let start_options = {
|
||||
\ 'callback': funcref("s:callback"),
|
||||
\ 'exit_cb': funcref("s:exit_cb"),
|
||||
\ 'close_cb': funcref("s:close_cb"),
|
||||
\ }
|
||||
\ 'callback': function('s:callback', [], state),
|
||||
\ 'exit_cb': function('s:exit_cb', [], state),
|
||||
\ 'close_cb': function('s:close_cb', [], state)
|
||||
\ }
|
||||
|
||||
if has_key(result, 'stdin_content')
|
||||
let l:tmpname = tempname()
|
||||
@ -201,18 +246,18 @@ function! s:async_guru(args) abort
|
||||
let l:start_options.in_name = l:tmpname
|
||||
endif
|
||||
|
||||
call go#statusline#Update(status_dir, {
|
||||
call go#statusline#Update(state.status_dir, {
|
||||
\ 'desc': "current status",
|
||||
\ 'type': statusline_type,
|
||||
\ 'type': state.statusline_type,
|
||||
\ 'state': "analysing",
|
||||
\})
|
||||
|
||||
return job_start(result.cmd, start_options)
|
||||
return s:job_start(result.cmd, start_options)
|
||||
endfunc
|
||||
|
||||
" run_guru runs the given guru argument
|
||||
function! s:run_guru(args) abort
|
||||
if go#util#has_job()
|
||||
if has('nvim') || go#util#has_job()
|
||||
let res = s:async_guru(a:args)
|
||||
else
|
||||
let res = s:sync_guru(a:args)
|
||||
@ -273,7 +318,7 @@ function! go#guru#DescribeInfo() abort
|
||||
return
|
||||
endif
|
||||
|
||||
function! s:info(exit_val, output)
|
||||
function! s:info(exit_val, output, mode)
|
||||
if a:exit_val != 0
|
||||
return
|
||||
endif
|
||||
@ -448,10 +493,6 @@ function! go#guru#Referrers(selected) abort
|
||||
call s:run_guru(args)
|
||||
endfunction
|
||||
|
||||
function! go#guru#SameIdsTimer() abort
|
||||
call timer_start(200, function('go#guru#SameIds'), {'repeat': -1})
|
||||
endfunction
|
||||
|
||||
function! go#guru#SameIds() abort
|
||||
" we use matchaddpos() which was introduce with 7.4.330, be sure we have
|
||||
" it: http://ftp.vim.org/vim/patches/7.4/7.4.330
|
||||
@ -479,7 +520,7 @@ function! go#guru#SameIds() abort
|
||||
call s:run_guru(args)
|
||||
endfunction
|
||||
|
||||
function! s:same_ids_highlight(exit_val, output) abort
|
||||
function! s:same_ids_highlight(exit_val, output, mode) abort
|
||||
call go#guru#ClearSameIds() " run after calling guru to reduce flicker.
|
||||
|
||||
if a:output[0] !=# '{'
|
||||
|
||||
@ -1,9 +1,38 @@
|
||||
" Spawn returns callbacks to be used with job_start. It's abstracted to be
|
||||
" used with various go command, such as build, test, install, etc.. This avoid
|
||||
" us to write the same callback over and over for some commands. It's fully
|
||||
" customizable so each command can change it to it's own logic.
|
||||
" Spawn returns callbacks to be used with job_start. It is abstracted to be
|
||||
" used with various go commands, such as build, test, install, etc.. This
|
||||
" allows us to avoid writing the same callback over and over for some
|
||||
" commands. It's fully customizable so each command can change it to it's own
|
||||
" logic.
|
||||
"
|
||||
" args is a dictionary with the these keys:
|
||||
" 'cmd':
|
||||
" The value to pass to job_start().
|
||||
" 'bang':
|
||||
" Set to 0 to jump to the first error in the error list.
|
||||
" Defaults to 0.
|
||||
" 'for':
|
||||
" The g:go_list_type_command key to use to get the error list type to use.
|
||||
" Defaults to '_job'
|
||||
" 'complete':
|
||||
" A function to call after the job exits and the channel is closed. The
|
||||
" function will be passed three arguments: the job, its exit code, and the
|
||||
" list of messages received from the channel. The default value will
|
||||
" process the messages and manage the error list after the job exits and
|
||||
" the channel is closed.
|
||||
|
||||
" The return value is a dictionary with these keys:
|
||||
" 'callback':
|
||||
" A function suitable to be passed as a job callback handler. See
|
||||
" job-callback.
|
||||
" 'exit_cb':
|
||||
" A function suitable to be passed as a job exit_cb handler. See
|
||||
" job-exit_cb.
|
||||
" 'close_cb':
|
||||
" A function suitable to be passed as a job close_cb handler. See
|
||||
" job-close_cb.
|
||||
function go#job#Spawn(args)
|
||||
let cbs = {
|
||||
let cbs = {}
|
||||
let state = {
|
||||
\ 'winnr': winnr(),
|
||||
\ 'dir': getcwd(),
|
||||
\ 'jobdir': fnameescape(expand("%:p:h")),
|
||||
@ -11,34 +40,38 @@ function go#job#Spawn(args)
|
||||
\ 'args': a:args.cmd,
|
||||
\ 'bang': 0,
|
||||
\ 'for': "_job",
|
||||
\ }
|
||||
\ 'exited': 0,
|
||||
\ 'exit_status': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'errorformat': &errorformat
|
||||
\ }
|
||||
|
||||
if has_key(a:args, 'bang')
|
||||
let cbs.bang = a:args.bang
|
||||
let state.bang = a:args.bang
|
||||
endif
|
||||
|
||||
if has_key(a:args, 'for')
|
||||
let cbs.for = a:args.for
|
||||
let state.for = a:args.for
|
||||
endif
|
||||
|
||||
" add final callback to be called if async job is finished
|
||||
" The signature should be in form: func(job, exit_status, messages)
|
||||
if has_key(a:args, 'custom_cb')
|
||||
let cbs.custom_cb = a:args.custom_cb
|
||||
endif
|
||||
|
||||
if has_key(a:args, 'error_info_cb')
|
||||
let cbs.error_info_cb = a:args.error_info_cb
|
||||
endif
|
||||
|
||||
function cbs.callback(chan, msg) dict
|
||||
call add(self.messages, a:msg)
|
||||
" do nothing in state.complete by default.
|
||||
function state.complete(job, exit_status, data)
|
||||
endfunction
|
||||
|
||||
function cbs.exit_cb(job, exitval) dict
|
||||
if has_key(self, 'error_info_cb')
|
||||
call self.error_info_cb(a:job, a:exitval, self.messages)
|
||||
endif
|
||||
if has_key(a:args, 'complete')
|
||||
let state.complete = a:args.complete
|
||||
endif
|
||||
|
||||
function! s:callback(chan, msg) dict
|
||||
call add(self.messages, a:msg)
|
||||
endfunction
|
||||
" explicitly bind callback to state so that within it, self will
|
||||
" always refer to state. See :help Partial for more information.
|
||||
let cbs.callback = function('s:callback', [], state)
|
||||
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exit_status = a:exitval
|
||||
let self.exited = 1
|
||||
|
||||
if get(g:, 'go_echo_command_info', 1)
|
||||
if a:exitval == 0
|
||||
@ -48,55 +81,69 @@ function go#job#Spawn(args)
|
||||
endif
|
||||
endif
|
||||
|
||||
if has_key(self, 'custom_cb')
|
||||
call self.custom_cb(a:job, a:exitval, self.messages)
|
||||
if self.closed
|
||||
call self.complete(a:job, self.exit_status, self.messages)
|
||||
call self.show_errors(a:job, self.exit_status, self.messages)
|
||||
endif
|
||||
endfunction
|
||||
" explicitly bind exit_cb to state so that within it, self will always refer
|
||||
" to state. See :help Partial for more information.
|
||||
let cbs.exit_cb = function('s:exit_cb', [], state)
|
||||
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if self.exited
|
||||
let job = ch_getjob(a:ch)
|
||||
call self.complete(job, self.exit_status, self.messages)
|
||||
call self.show_errors(job, self.exit_status, self.messages)
|
||||
endif
|
||||
endfunction
|
||||
" explicitly bind close_cb to state so that within it, self will
|
||||
" always refer to state. See :help Partial for more information.
|
||||
let cbs.close_cb = function('s:close_cb', [], state)
|
||||
|
||||
function state.show_errors(job, exit_status, data)
|
||||
let l:listtype = go#list#Type(self.for)
|
||||
if a:exitval == 0
|
||||
if a:exit_status == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
call self.show_errors(l:listtype)
|
||||
endfunction
|
||||
let l:listtype = go#list#Type(self.for)
|
||||
if len(a:data) == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
let out = join(self.messages, "\n")
|
||||
|
||||
function cbs.show_errors(listtype) dict
|
||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||
try
|
||||
" parse the errors relative to self.jobdir
|
||||
execute cd self.jobdir
|
||||
let errors = go#tool#ParseErrors(self.messages)
|
||||
let errors = go#tool#FilterValids(errors)
|
||||
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
|
||||
let errors = go#list#Get(l:listtype)
|
||||
finally
|
||||
execute cd . fnameescape(self.dir)
|
||||
endtry
|
||||
|
||||
if !len(errors)
|
||||
|
||||
if empty(errors)
|
||||
" failed to parse errors, output the original content
|
||||
call go#util#EchoError(self.messages + [self.dir])
|
||||
return
|
||||
endif
|
||||
|
||||
if self.winnr == winnr()
|
||||
call go#list#Populate(a:listtype, errors, join(self.args))
|
||||
call go#list#Window(a:listtype, len(errors))
|
||||
if !empty(errors) && !self.bang
|
||||
call go#list#JumpToFirst(a:listtype)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
if !self.bang
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" override callback handler if user provided it
|
||||
if has_key(a:args, 'callback')
|
||||
let cbs.callback = a:args.callback
|
||||
endif
|
||||
|
||||
" override exit callback handler if user provided it
|
||||
if has_key(a:args, 'exit_cb')
|
||||
let cbs.exit_cb = a:args.exit_cb
|
||||
endif
|
||||
|
||||
return cbs
|
||||
endfunction
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
@ -62,6 +62,7 @@ function! s:spawn(bang, desc, for, args) abort
|
||||
\ 'status_dir' : status_dir,
|
||||
\ 'started_at' : started_at,
|
||||
\ 'for' : a:for,
|
||||
\ 'errorformat': &errorformat,
|
||||
\ }
|
||||
|
||||
" execute go build in the files directory
|
||||
@ -125,7 +126,6 @@ function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
let l:listtype = go#list#Type(self.for)
|
||||
if a:exit_status == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
|
||||
let self.state = "SUCCESS"
|
||||
|
||||
@ -143,8 +143,9 @@ function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
call go#util#EchoError("[" . self.status_type . "] FAILED")
|
||||
endif
|
||||
|
||||
let errors = go#tool#ParseErrors(std_combined)
|
||||
let errors = go#tool#FilterValids(errors)
|
||||
" parse the errors relative to self.jobdir
|
||||
call go#list#ParseFormat(l:listtype, self.errorformat, std_combined, self.for)
|
||||
let errors = go#list#Get(l:listtype)
|
||||
|
||||
execute cd . fnameescape(dir)
|
||||
|
||||
@ -156,7 +157,6 @@ function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
|
||||
" if we are still in the same windows show the list
|
||||
if self.winnr == winnr()
|
||||
call go#list#Populate(l:listtype, errors, self.desc)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
if !empty(errors) && !self.bang
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
|
||||
@ -10,8 +10,8 @@ if !exists("g:go_metalinter_enabled")
|
||||
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
|
||||
endif
|
||||
|
||||
if !exists("g:go_metalinter_excludes")
|
||||
let g:go_metalinter_excludes = []
|
||||
if !exists("g:go_metalinter_disabled")
|
||||
let g:go_metalinter_disabled = []
|
||||
endif
|
||||
|
||||
if !exists("g:go_golint_bin")
|
||||
@ -24,9 +24,9 @@ endif
|
||||
|
||||
function! go#lint#Gometa(autosave, ...) abort
|
||||
if a:0 == 0
|
||||
let goargs = shellescape(expand('%:p:h'))
|
||||
let goargs = [expand('%:p:h')]
|
||||
else
|
||||
let goargs = go#util#Shelljoin(a:000)
|
||||
let goargs = a:000
|
||||
endif
|
||||
|
||||
let bin_path = go#path#CheckBinPath("gometalinter")
|
||||
@ -44,8 +44,8 @@ function! go#lint#Gometa(autosave, ...) abort
|
||||
let cmd += ["--enable=".linter]
|
||||
endfor
|
||||
|
||||
for exclude in g:go_metalinter_excludes
|
||||
let cmd += ["--exclude=".exclude]
|
||||
for linter in g:go_metalinter_disabled
|
||||
let cmd += ["--disable=".linter]
|
||||
endfor
|
||||
|
||||
" gometalinter has a --tests flag to tell its linters whether to run
|
||||
@ -54,14 +54,20 @@ function! go#lint#Gometa(autosave, ...) abort
|
||||
" test files. One example of a linter that will not run against tests if
|
||||
" we do not specify this flag is errcheck.
|
||||
let cmd += ["--tests"]
|
||||
|
||||
" path
|
||||
let cmd += [expand('%:p:h')]
|
||||
else
|
||||
" the user wants something else, let us use it.
|
||||
let cmd += split(g:go_metalinter_command, " ")
|
||||
endif
|
||||
|
||||
if a:autosave
|
||||
" redraw so that any messages that were displayed while writing the file
|
||||
" will be cleared
|
||||
redraw
|
||||
|
||||
" Include only messages for the active buffer for autosave.
|
||||
let cmd += [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
|
||||
endif
|
||||
|
||||
" gometalinter has a default deadline of 5 seconds.
|
||||
"
|
||||
" For async mode (s:lint_job), we want to override the default deadline only
|
||||
@ -78,29 +84,27 @@ function! go#lint#Gometa(autosave, ...) abort
|
||||
let cmd += ["--deadline=" . deadline]
|
||||
endif
|
||||
|
||||
call s:lint_job({'cmd': cmd})
|
||||
let cmd += goargs
|
||||
|
||||
call s:lint_job({'cmd': cmd}, a:autosave)
|
||||
return
|
||||
endif
|
||||
|
||||
" We're calling gometalinter synchronously.
|
||||
|
||||
let cmd += ["--deadline=" . get(g:, 'go_metalinter_deadline', "5s")]
|
||||
|
||||
let cmd += goargs
|
||||
|
||||
let [l:out, l:err] = go#util#Exec(cmd)
|
||||
|
||||
if a:autosave
|
||||
" include only messages for the active buffer
|
||||
let cmd += ["--include='^" . expand('%:p') . ".*$'"]
|
||||
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
|
||||
else
|
||||
let l:listtype = go#list#Type("GoMetaLinter")
|
||||
endif
|
||||
|
||||
|
||||
let meta_command = join(cmd, " ")
|
||||
|
||||
let out = go#util#System(meta_command)
|
||||
|
||||
let l:listtype = go#list#Type("GoMetaLinter")
|
||||
if go#util#ShellError() == 0
|
||||
redraw | echo
|
||||
if l:err == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
echon "vim-go: " | echohl Function | echon "[metalinter] PASS" | echohl None
|
||||
else
|
||||
" GoMetaLinter can output one of the two, so we look for both:
|
||||
@ -142,7 +146,7 @@ function! go#lint#Golint(...) abort
|
||||
endif
|
||||
|
||||
let l:listtype = go#list#Type("GoLint")
|
||||
call go#list#Parse(l:listtype, out)
|
||||
call go#list#Parse(l:listtype, out, "GoLint")
|
||||
let errors = go#list#Get(l:listtype)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
@ -161,8 +165,9 @@ function! go#lint#Vet(bang, ...) abort
|
||||
|
||||
let l:listtype = go#list#Type("GoVet")
|
||||
if go#util#ShellError() != 0
|
||||
let errors = go#tool#ParseErrors(split(out, '\n'))
|
||||
call go#list#Populate(l:listtype, errors, 'Vet')
|
||||
let errorformat="%-Gexit status %\\d%\\+," . &errorformat
|
||||
call go#list#ParseFormat(l:listtype, l:errorformat, out, "GoVet")
|
||||
let errors = go#list#Get(l:listtype)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
if !empty(errors) && !a:bang
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
@ -170,7 +175,6 @@ function! go#lint#Vet(bang, ...) abort
|
||||
echon "vim-go: " | echohl ErrorMsg | echon "[vet] FAIL" | echohl None
|
||||
else
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
redraw | echon "vim-go: " | echohl Function | echon "[vet] PASS" | echohl None
|
||||
endif
|
||||
endfunction
|
||||
@ -223,7 +227,6 @@ function! go#lint#Errcheck(...) abort
|
||||
endif
|
||||
else
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
echon "vim-go: " | echohl Function | echon "[errcheck] PASS" | echohl None
|
||||
endif
|
||||
|
||||
@ -240,11 +243,19 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
|
||||
call go#util#EchoProgress("auto metalinter enabled")
|
||||
endfunction
|
||||
|
||||
function s:lint_job(args)
|
||||
let status_dir = expand('%:p:h')
|
||||
let started_at = reltime()
|
||||
function! s:lint_job(args, autosave)
|
||||
let state = {
|
||||
\ 'status_dir': expand('%:p:h'),
|
||||
\ 'started_at': reltime(),
|
||||
\ 'messages': [],
|
||||
\ 'exited': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'exit_status': 0,
|
||||
\ 'winnr': winnr(),
|
||||
\ 'autosave': a:autosave
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(status_dir, {
|
||||
call go#statusline#Update(state.status_dir, {
|
||||
\ 'desc': "current status",
|
||||
\ 'type': "gometalinter",
|
||||
\ 'state': "analysing",
|
||||
@ -253,32 +264,20 @@ function s:lint_job(args)
|
||||
" autowrite is not enabled for jobs
|
||||
call go#cmd#autowrite()
|
||||
|
||||
let l:listtype = go#list#Type("GoMetaLinter")
|
||||
let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
|
||||
if a:autosave
|
||||
let state.listtype = go#list#Type("GoMetaLinterAutoSave")
|
||||
else
|
||||
let state.listtype = go#list#Type("GoMetaLinter")
|
||||
endif
|
||||
|
||||
function! s:callback(chan, msg) closure
|
||||
let old_errorformat = &errorformat
|
||||
let &errorformat = l:errformat
|
||||
try
|
||||
if l:listtype == "locationlist"
|
||||
lad a:msg
|
||||
elseif l:listtype == "quickfix"
|
||||
caddexpr a:msg
|
||||
endif
|
||||
finally
|
||||
let &errorformat = old_errorformat
|
||||
endtry
|
||||
|
||||
" TODO(jinleileiking): give a configure to jump or not
|
||||
let l:winnr = winnr()
|
||||
|
||||
let errors = go#list#Get(l:listtype)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
|
||||
exe l:winnr . "wincmd w"
|
||||
function! s:callback(chan, msg) dict closure
|
||||
call add(self.messages, a:msg)
|
||||
endfunction
|
||||
|
||||
function! s:exit_cb(job, exitval) closure
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
let self.exit_status = a:exitval
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
\ 'type': "gometaliner",
|
||||
@ -289,22 +288,50 @@ function s:lint_job(args)
|
||||
let status.state = "failed"
|
||||
endif
|
||||
|
||||
let elapsed_time = reltimestr(reltime(started_at))
|
||||
let elapsed_time = reltimestr(reltime(self.started_at))
|
||||
" strip whitespace
|
||||
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||
let status.state .= printf(" (%ss)", elapsed_time)
|
||||
|
||||
call go#statusline#Update(status_dir, status)
|
||||
call go#statusline#Update(self.status_dir, status)
|
||||
|
||||
let errors = go#list#Get(l:listtype)
|
||||
if empty(errors)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
elseif has("patch-7.4.2200")
|
||||
if l:listtype == 'quickfix'
|
||||
call setqflist([], 'a', {'title': 'GoMetaLinter'})
|
||||
else
|
||||
call setloclist(0, [], 'a', {'title': 'GoMetaLinter'})
|
||||
endif
|
||||
if self.closed
|
||||
call self.show_errors()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if self.exited
|
||||
call self.show_errors()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function state.show_errors()
|
||||
let l:winnr = winnr()
|
||||
|
||||
" make sure the current window is the window from which gometalinter was
|
||||
" run when the listtype is locationlist so that the location list for the
|
||||
" correct window will be populated.
|
||||
if self.listtype == 'locationlist'
|
||||
exe self.winnr . "wincmd w"
|
||||
endif
|
||||
|
||||
let l:errorformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
|
||||
call go#list#ParseFormat(self.listtype, l:errorformat, self.messages, 'GoMetaLinter')
|
||||
|
||||
let errors = go#list#Get(self.listtype)
|
||||
call go#list#Window(self.listtype, len(errors))
|
||||
|
||||
" move to the window that was active before processing the errors, because
|
||||
" the user may have moved around within the window or even moved to a
|
||||
" different window since saving. Moving back to current window as of the
|
||||
" start of this function avoids the perception that the quickfix window
|
||||
" steals focus when linting takes a while.
|
||||
if self.autosave
|
||||
exe l:winnr . "wincmd w"
|
||||
endif
|
||||
|
||||
if get(g:, 'go_echo_command_info', 1)
|
||||
@ -312,15 +339,16 @@ function s:lint_job(args)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" explicitly bind the callbacks to state so that self within them always
|
||||
" refers to state. See :help Partial for more information.
|
||||
let start_options = {
|
||||
\ 'callback': funcref("s:callback"),
|
||||
\ 'exit_cb': funcref("s:exit_cb"),
|
||||
\ 'callback': funcref("s:callback", [], state),
|
||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
||||
\ 'close_cb': funcref("s:close_cb", [], state),
|
||||
\ }
|
||||
|
||||
call job_start(a:args.cmd, start_options)
|
||||
|
||||
call go#list#Clean(l:listtype)
|
||||
|
||||
if get(g:, 'go_echo_command_info', 1)
|
||||
call go#util#EchoProgress("linting started ...")
|
||||
endif
|
||||
|
||||
131
autoload/go/lint_test.vim
Normal file
131
autoload/go/lint_test.vim
Normal file
@ -0,0 +1,131 @@
|
||||
func! Test_Gometa() abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
||||
\ ]
|
||||
|
||||
" clear the quickfix lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
|
||||
" be autoloaded and the default for g:go_metalinter_enabled will be set so
|
||||
" we can capture it to restore it after the test is run.
|
||||
call go#lint#ToggleMetaLinterAutoSave()
|
||||
" And restore it back to its previous value
|
||||
call go#lint#ToggleMetaLinterAutoSave()
|
||||
|
||||
let orig_go_metalinter_enabled = g:go_metalinter_enabled
|
||||
let g:go_metalinter_enabled = ['golint']
|
||||
|
||||
call go#lint#Gometa(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)
|
||||
let g:go_metalinter_enabled = orig_go_metalinter_enabled
|
||||
endfunc
|
||||
|
||||
func! Test_GometaWithDisabled() abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%')+1, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
||||
\ ]
|
||||
|
||||
" clear the quickfix lists
|
||||
call setqflist([], 'r')
|
||||
|
||||
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
|
||||
" be autoloaded and the default for g:go_metalinter_disabled will be set so
|
||||
" we can capture it to restore it after the test is run.
|
||||
call go#lint#ToggleMetaLinterAutoSave()
|
||||
" And restore it back to its previous value
|
||||
call go#lint#ToggleMetaLinterAutoSave()
|
||||
|
||||
let orig_go_metalinter_disabled = g:go_metalinter_disabled
|
||||
let g:go_metalinter_disabled = ['vet']
|
||||
|
||||
call go#lint#Gometa(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)
|
||||
let g:go_metalinter_disabled = orig_go_metalinter_disabled
|
||||
endfunc
|
||||
|
||||
func! Test_GometaAutoSave() abort
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': bufnr('%'), 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
|
||||
\ ]
|
||||
|
||||
let winnr = winnr()
|
||||
|
||||
" clear the location lists
|
||||
call setloclist(l:winnr, [], 'r')
|
||||
|
||||
" call go#lint#ToggleMetaLinterAutoSave from lint.vim so that the file will
|
||||
" be autoloaded and the default for g:go_metalinter_autosave_enabled will be
|
||||
" set so we can capture it to restore it after the test is run.
|
||||
call go#lint#ToggleMetaLinterAutoSave()
|
||||
" And restore it back to its previous value
|
||||
call go#lint#ToggleMetaLinterAutoSave()
|
||||
|
||||
let orig_go_metalinter_autosave_enabled = g:go_metalinter_autosave_enabled
|
||||
let g:go_metalinter_autosave_enabled = ['golint']
|
||||
|
||||
call go#lint#Gometa(1)
|
||||
|
||||
let actual = getloclist(l:winnr)
|
||||
let start = reltime()
|
||||
while len(actual) == 0 && reltimefloat(reltime(start)) < 10
|
||||
sleep 100m
|
||||
let actual = getloclist(l:winnr)
|
||||
endwhile
|
||||
|
||||
call gotest#assert_quickfix(actual, expected)
|
||||
let g:go_metalinter_autosave_enabled = orig_go_metalinter_autosave_enabled
|
||||
endfunc
|
||||
|
||||
func! Test_Vet()
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/lint'
|
||||
silent exe 'e ' . $GOPATH . '/src/vet/vet.go'
|
||||
compiler go
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 7, 'bufnr': bufnr('%'), 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'arg str for printf verb %d 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)
|
||||
endfunc
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
@ -18,14 +18,7 @@ function! go#list#Window(listtype, ...) abort
|
||||
" location list increases/decreases, cwindow will not resize when a new
|
||||
" updated height is passed. lopen in the other hand resizes the screen.
|
||||
if !a:0 || a:1 == 0
|
||||
let autoclose_window = get(g:, 'go_list_autoclose', 1)
|
||||
if autoclose_window
|
||||
if a:listtype == "locationlist"
|
||||
lclose
|
||||
else
|
||||
cclose
|
||||
endif
|
||||
endif
|
||||
call go#list#Close(a:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
@ -79,13 +72,7 @@ function! go#list#ParseFormat(listtype, errformat, items, title) abort
|
||||
" parse and populate the location list
|
||||
let &errorformat = a:errformat
|
||||
try
|
||||
if a:listtype == "locationlist"
|
||||
lgetexpr a:items
|
||||
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
|
||||
else
|
||||
cgetexpr a:items
|
||||
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
|
||||
endif
|
||||
call go#list#Parse(a:listtype, a:items, a:title)
|
||||
finally
|
||||
"restore back
|
||||
let &errorformat = old_errorformat
|
||||
@ -94,11 +81,13 @@ endfunction
|
||||
|
||||
" Parse parses the given items based on the global errorformat and
|
||||
" populates the list.
|
||||
function! go#list#Parse(listtype, items) abort
|
||||
function! go#list#Parse(listtype, items, title) abort
|
||||
if a:listtype == "locationlist"
|
||||
lgetexpr a:items
|
||||
if has("patch-7.4.2200") | call setloclist(0, [], 'a', {'title': a:title}) | endif
|
||||
else
|
||||
cgetexpr a:items
|
||||
if has("patch-7.4.2200") | call setqflist([], 'a', {'title': a:title}) | endif
|
||||
endif
|
||||
endfunction
|
||||
|
||||
@ -111,13 +100,29 @@ function! go#list#JumpToFirst(listtype) abort
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" Clean cleans the location list
|
||||
" Clean cleans and closes the location list
|
||||
function! go#list#Clean(listtype) abort
|
||||
if a:listtype == "locationlist"
|
||||
lex []
|
||||
else
|
||||
cex []
|
||||
endif
|
||||
|
||||
call go#list#Close(a:listtype)
|
||||
endfunction
|
||||
|
||||
" Close closes the location list
|
||||
function! go#list#Close(listtype) abort
|
||||
let autoclose_window = get(g:, 'go_list_autoclose', 1)
|
||||
if !autoclose_window
|
||||
return
|
||||
endif
|
||||
|
||||
if a:listtype == "locationlist"
|
||||
lclose
|
||||
else
|
||||
cclose
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:listtype(listtype) abort
|
||||
@ -137,21 +142,22 @@ endfunction
|
||||
" single file or buffer. Keys that begin with an underscore are not supported
|
||||
" in g:go_list_type_commands.
|
||||
let s:default_list_type_commands = {
|
||||
\ "GoBuild": "quickfix",
|
||||
\ "GoErrCheck": "quickfix",
|
||||
\ "GoFmt": "locationlist",
|
||||
\ "GoGenerate": "quickfix",
|
||||
\ "GoInstall": "quickfix",
|
||||
\ "GoLint": "quickfix",
|
||||
\ "GoMetaLinter": "quickfix",
|
||||
\ "GoModifyTags": "locationlist",
|
||||
\ "GoRename": "quickfix",
|
||||
\ "GoRun": "quickfix",
|
||||
\ "GoTest": "quickfix",
|
||||
\ "GoVet": "quickfix",
|
||||
\ "_guru": "locationlist",
|
||||
\ "_term": "locationlist",
|
||||
\ "_job": "locationlist",
|
||||
\ "GoBuild": "quickfix",
|
||||
\ "GoErrCheck": "quickfix",
|
||||
\ "GoFmt": "locationlist",
|
||||
\ "GoGenerate": "quickfix",
|
||||
\ "GoInstall": "quickfix",
|
||||
\ "GoLint": "quickfix",
|
||||
\ "GoMetaLinter": "quickfix",
|
||||
\ "GoMetaLinterAutoSave": "locationlist",
|
||||
\ "GoModifyTags": "locationlist",
|
||||
\ "GoRename": "quickfix",
|
||||
\ "GoRun": "quickfix",
|
||||
\ "GoTest": "quickfix",
|
||||
\ "GoVet": "quickfix",
|
||||
\ "_guru": "locationlist",
|
||||
\ "_term": "locationlist",
|
||||
\ "_job": "locationlist",
|
||||
\ }
|
||||
|
||||
function! go#list#Type(for) abort
|
||||
|
||||
@ -54,8 +54,14 @@ function! go#package#Paths() abort
|
||||
return dirs
|
||||
endfunction
|
||||
|
||||
let s:import_paths = {}
|
||||
" ImportPath returns the import path in the current directory it was executed
|
||||
function! go#package#ImportPath() abort
|
||||
let dir = expand("%:p:h")
|
||||
if has_key(s:import_paths, dir)
|
||||
return s:import_paths[dir]
|
||||
endif
|
||||
|
||||
let out = go#tool#ExecuteInDir("go list")
|
||||
if go#util#ShellError() != 0
|
||||
return -1
|
||||
@ -69,6 +75,8 @@ function! go#package#ImportPath() abort
|
||||
return -1
|
||||
endif
|
||||
|
||||
let s:import_paths[dir] = import_path
|
||||
|
||||
return import_path
|
||||
endfunction
|
||||
|
||||
|
||||
@ -45,9 +45,9 @@ function! go#path#Default() abort
|
||||
return $GOPATH
|
||||
endfunction
|
||||
|
||||
" HasPath checks whether the given path exists in GOPATH environment variable
|
||||
" s:HasPath checks whether the given path exists in GOPATH environment variable
|
||||
" or not
|
||||
function! go#path#HasPath(path) abort
|
||||
function! s:HasPath(path) abort
|
||||
let go_paths = split(go#path#Default(), go#util#PathListSep())
|
||||
let last_char = strlen(a:path) - 1
|
||||
|
||||
@ -94,11 +94,11 @@ function! go#path#Detect() abort
|
||||
" gb vendor plugin
|
||||
" (https://github.com/constabulary/gb/tree/master/cmd/gb-vendor)
|
||||
let gb_vendor_root = src_path . "vendor" . go#util#PathSep()
|
||||
if isdirectory(gb_vendor_root) && !go#path#HasPath(gb_vendor_root)
|
||||
if isdirectory(gb_vendor_root) && !s:HasPath(gb_vendor_root)
|
||||
let gopath = gb_vendor_root . go#util#PathListSep() . gopath
|
||||
endif
|
||||
|
||||
if !go#path#HasPath(src_path)
|
||||
if !s:HasPath(src_path)
|
||||
let gopath = src_path . go#util#PathListSep() . gopath
|
||||
endif
|
||||
endif
|
||||
@ -108,7 +108,7 @@ function! go#path#Detect() abort
|
||||
if !empty(godeps_root)
|
||||
let godeps_path = join([fnamemodify(godeps_root, ':p:h:h'), "Godeps", "_workspace" ], go#util#PathSep())
|
||||
|
||||
if !go#path#HasPath(godeps_path)
|
||||
if !s:HasPath(godeps_path)
|
||||
let gopath = godeps_path . go#util#PathListSep() . gopath
|
||||
endif
|
||||
endif
|
||||
@ -164,7 +164,7 @@ function! go#path#CheckBinPath(binpath) abort
|
||||
let $PATH = old_path
|
||||
|
||||
if go#util#IsUsingCygwinShell() == 1
|
||||
return go#path#CygwinPath(binpath)
|
||||
return s:CygwinPath(binpath)
|
||||
endif
|
||||
|
||||
return binpath
|
||||
@ -183,13 +183,13 @@ function! go#path#CheckBinPath(binpath) abort
|
||||
let $PATH = old_path
|
||||
|
||||
if go#util#IsUsingCygwinShell() == 1
|
||||
return go#path#CygwinPath(a:binpath)
|
||||
return s:CygwinPath(a:binpath)
|
||||
endif
|
||||
|
||||
return go_bin_path . go#util#PathSep() . basename
|
||||
endfunction
|
||||
|
||||
function! go#path#CygwinPath(path)
|
||||
function! s:CygwinPath(path)
|
||||
return substitute(a:path, '\\', '/', "g")
|
||||
endfunction
|
||||
|
||||
|
||||
@ -72,14 +72,23 @@ function! go#rename#Rename(bang, ...) abort
|
||||
endfunction
|
||||
|
||||
function s:rename_job(args)
|
||||
let messages = []
|
||||
function! s:callback(chan, msg) closure
|
||||
call add(messages, a:msg)
|
||||
let state = {
|
||||
\ 'exited': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'exitval': 0,
|
||||
\ 'messages': [],
|
||||
\ 'status_dir': expand('%:p:h'),
|
||||
\ 'bang': a:args.bang
|
||||
\ }
|
||||
|
||||
function! s:callback(chan, msg) dict
|
||||
call add(self.messages, a:msg)
|
||||
endfunction
|
||||
|
||||
let status_dir = expand('%:p:h')
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
let self.exitval = a:exitval
|
||||
|
||||
function! s:exit_cb(job, exitval) closure
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
\ 'type': "gorename",
|
||||
@ -90,17 +99,30 @@ function s:rename_job(args)
|
||||
let status.state = "failed"
|
||||
endif
|
||||
|
||||
call go#statusline#Update(status_dir, status)
|
||||
call go#statusline#Update(self.status_dir, status)
|
||||
|
||||
call s:parse_errors(a:exitval, a:args.bang, messages)
|
||||
if self.closed
|
||||
call s:parse_errors(self.exitval, self.bang, self.messages)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if self.exited
|
||||
call s:parse_errors(self.exitval, self.bang, self.messages)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" explicitly bind the callbacks to state so that self within them always
|
||||
" refers to state. See :help Partial for more information.
|
||||
let start_options = {
|
||||
\ 'callback': funcref("s:callback"),
|
||||
\ 'exit_cb': funcref("s:exit_cb"),
|
||||
\ 'callback': funcref("s:callback", [], state),
|
||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
||||
\ 'close_cb': funcref("s:close_cb", [], state),
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(status_dir, {
|
||||
call go#statusline#Update(state.status_dir, {
|
||||
\ 'desc': "current status",
|
||||
\ 'type': "gorename",
|
||||
\ 'state': "started",
|
||||
@ -138,7 +160,6 @@ function s:parse_errors(exit_val, bang, out)
|
||||
" strip out newline on the end that gorename puts. If we don't remove, it
|
||||
" will trigger the 'Hit ENTER to continue' prompt
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
call go#util#EchoSuccess(a:out[0])
|
||||
|
||||
" refresh the buffer so we can see the new content
|
||||
|
||||
@ -24,7 +24,7 @@ function! go#template#create() abort
|
||||
let l:template_file = get(g:, 'go_template_file', "hello_world.go")
|
||||
endif
|
||||
let l:template_path = go#util#Join(l:root_dir, "templates", l:template_file)
|
||||
silent exe '0r ' . fnameescape(l:template_path)
|
||||
silent exe 'keepalt 0r ' . fnameescape(l:template_path)
|
||||
elseif l:package_name == -1 && l:go_template_use_pkg == 1
|
||||
" cwd is now the dir of the package
|
||||
let l:path = fnamemodify(getcwd(), ':t')
|
||||
|
||||
@ -2,9 +2,6 @@ if has('nvim') && !exists("g:go_term_mode")
|
||||
let g:go_term_mode = 'vsplit'
|
||||
endif
|
||||
|
||||
" s:jobs is a global reference to all jobs started with new()
|
||||
let s:jobs = {}
|
||||
|
||||
" new creates a new terminal with the given command. Mode is set based on the
|
||||
" global variable g:go_term_mode, which is by default set to :vsplit
|
||||
function! go#term#new(bang, cmd) abort
|
||||
@ -18,8 +15,14 @@ function! go#term#newmode(bang, cmd, mode) abort
|
||||
let mode = g:go_term_mode
|
||||
endif
|
||||
|
||||
let state = {
|
||||
\ 'cmd': a:cmd,
|
||||
\ 'bang' : a:bang,
|
||||
\ 'winid': win_getid(winnr()),
|
||||
\ 'stdout': []
|
||||
\ }
|
||||
|
||||
" execute go build in the files directory
|
||||
let l:winnr = winnr()
|
||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||
let dir = getcwd()
|
||||
|
||||
@ -33,30 +36,27 @@ function! go#term#newmode(bang, cmd, mode) abort
|
||||
setlocal noswapfile
|
||||
setlocal nobuflisted
|
||||
|
||||
" explicitly bind callbacks to state so that within them, self will always
|
||||
" refer to state. See :help Partial for more information.
|
||||
"
|
||||
" Don't set an on_stderr, because it will be passed the same data as
|
||||
" on_stdout. See https://github.com/neovim/neovim/issues/2836
|
||||
let job = {
|
||||
\ 'stderr' : [],
|
||||
\ 'stdout' : [],
|
||||
\ 'bang' : a:bang,
|
||||
\ 'on_stdout': function('s:on_stdout'),
|
||||
\ 'on_stderr': function('s:on_stderr'),
|
||||
\ 'on_exit' : function('s:on_exit'),
|
||||
\ }
|
||||
\ 'on_stdout': function('s:on_stdout', [], state),
|
||||
\ 'on_exit' : function('s:on_exit', [], state),
|
||||
\ }
|
||||
|
||||
let id = termopen(a:cmd, job)
|
||||
let state.id = termopen(a:cmd, job)
|
||||
let state.termwinid = win_getid(winnr())
|
||||
|
||||
execute cd . fnameescape(dir)
|
||||
|
||||
let job.id = id
|
||||
let job.cmd = a:cmd
|
||||
startinsert
|
||||
|
||||
" resize new term if needed.
|
||||
let height = get(g:, 'go_term_height', winheight(0))
|
||||
let width = get(g:, 'go_term_width', winwidth(0))
|
||||
|
||||
" we are careful how to resize. for example it's vsplit we don't change
|
||||
" the height. The below command resizes the buffer
|
||||
|
||||
" Adjust the window width or height depending on whether it's a vertical or
|
||||
" horizontal split.
|
||||
if mode =~ "vertical" || mode =~ "vsplit" || mode =~ "vnew"
|
||||
exe 'vertical resize ' . width
|
||||
elseif mode =~ "split" || mode =~ "new"
|
||||
@ -64,77 +64,56 @@ function! go#term#newmode(bang, cmd, mode) abort
|
||||
endif
|
||||
|
||||
" we also need to resize the pty, so there you go...
|
||||
call jobresize(id, width, height)
|
||||
call jobresize(state.id, width, height)
|
||||
|
||||
let s:jobs[id] = job
|
||||
stopinsert
|
||||
call win_gotoid(state.winid)
|
||||
|
||||
if l:winnr !=# winnr()
|
||||
exe l:winnr . "wincmd w"
|
||||
endif
|
||||
|
||||
return id
|
||||
return state.id
|
||||
endfunction
|
||||
|
||||
function! s:on_stdout(job_id, data, event) dict abort
|
||||
if !has_key(s:jobs, a:job_id)
|
||||
return
|
||||
endif
|
||||
let job = s:jobs[a:job_id]
|
||||
|
||||
call extend(job.stdout, a:data)
|
||||
endfunction
|
||||
|
||||
function! s:on_stderr(job_id, data, event) dict abort
|
||||
if !has_key(s:jobs, a:job_id)
|
||||
return
|
||||
endif
|
||||
let job = s:jobs[a:job_id]
|
||||
|
||||
call extend(job.stderr, a:data)
|
||||
call extend(self.stdout, a:data)
|
||||
endfunction
|
||||
|
||||
function! s:on_exit(job_id, exit_status, event) dict abort
|
||||
if !has_key(s:jobs, a:job_id)
|
||||
return
|
||||
endif
|
||||
let job = s:jobs[a:job_id]
|
||||
|
||||
let l:listtype = go#list#Type("_term")
|
||||
|
||||
" usually there is always output so never branch into this clause
|
||||
if empty(job.stdout)
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
unlet s:jobs[a:job_id]
|
||||
if empty(self.stdout)
|
||||
call s:cleanlist(self.winid, l:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
let errors = go#tool#ParseErrors(job.stdout)
|
||||
let errors = go#tool#ParseErrors(self.stdout)
|
||||
let errors = go#tool#FilterValids(errors)
|
||||
|
||||
if !empty(errors)
|
||||
" close terminal we don't need it anymore
|
||||
" close terminal; we don't need it anymore
|
||||
call win_gotoid(self.termwinid)
|
||||
close
|
||||
|
||||
call go#list#Populate(l:listtype, errors, job.cmd)
|
||||
call win_gotoid(self.winid)
|
||||
|
||||
call go#list#Populate(l:listtype, errors, self.cmd)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
if !self.bang
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
endif
|
||||
unlet s:jobs[a:job_id]
|
||||
|
||||
return
|
||||
endif
|
||||
|
||||
" tests are passing clean the list and close the list. But we only can
|
||||
" close them from a normal view, so jump back, close the list and then
|
||||
" again jump back to the terminal
|
||||
wincmd p
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
wincmd p
|
||||
call s:cleanlist(self.winid, l:listtype)
|
||||
endfunction
|
||||
|
||||
unlet s:jobs[a:job_id]
|
||||
function! s:cleanlist(winid, listtype) abort
|
||||
" There are no errors. Clean and close the list. Jump to the window to which
|
||||
" the location list is attached, close the list, and then jump back to the
|
||||
" current window.
|
||||
let winid = win_getid(winnr())
|
||||
call win_gotoid(a:winid)
|
||||
call go#list#Clean(a:listtype)
|
||||
call win_gotoid(l:winid)
|
||||
endfunction
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
50
autoload/go/term_test.vim
Normal file
50
autoload/go/term_test.vim
Normal file
@ -0,0 +1,50 @@
|
||||
func! Test_GoTermNewMode()
|
||||
if !has('nvim')
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let l:filename = 'term/term.go'
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
exe 'cd ' . l:tmp . '/src/term'
|
||||
|
||||
let expected = expand('%:p')
|
||||
|
||||
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
||||
|
||||
set nosplitright
|
||||
call go#term#newmode(0, cmd, '')
|
||||
let actual = expand('%:p')
|
||||
call assert_equal(actual, l:expected)
|
||||
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
func! Test_GoTermNewMode_SplitRight()
|
||||
if !has('nvim')
|
||||
return
|
||||
endif
|
||||
|
||||
try
|
||||
let l:filename = 'term/term.go'
|
||||
let l:tmp = gotest#load_fixture(l:filename)
|
||||
exe 'cd ' . l:tmp . '/src/term'
|
||||
|
||||
let expected = expand('%:p')
|
||||
|
||||
let cmd = "go run ". go#util#Shelljoin(go#tool#Files())
|
||||
|
||||
set splitright
|
||||
call go#term#newmode(0, cmd, '')
|
||||
let actual = expand('%:p')
|
||||
call assert_equal(actual, l:expected)
|
||||
|
||||
finally
|
||||
call delete(l:tmp, 'rf')
|
||||
set nosplitright
|
||||
endtry
|
||||
endfunc
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
5
autoload/go/test-fixtures/cmd/bad.go
Normal file
5
autoload/go/test-fixtures/cmd/bad.go
Normal file
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
notafunc()
|
||||
}
|
||||
7
autoload/go/test-fixtures/lint/src/foo/foo.go
Normal file
7
autoload/go/test-fixtures/lint/src/foo/foo.go
Normal file
@ -0,0 +1,7 @@
|
||||
package foo
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MissingFooDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
||||
7
autoload/go/test-fixtures/lint/src/lint/lint.go
Normal file
7
autoload/go/test-fixtures/lint/src/lint/lint.go
Normal file
@ -0,0 +1,7 @@
|
||||
package lint
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MissingDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
||||
7
autoload/go/test-fixtures/lint/src/lint/quux.go
Normal file
7
autoload/go/test-fixtures/lint/src/lint/quux.go
Normal file
@ -0,0 +1,7 @@
|
||||
package lint
|
||||
|
||||
import "fmt"
|
||||
|
||||
func AlsoMissingDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
||||
8
autoload/go/test-fixtures/lint/src/vet/vet.go
Normal file
8
autoload/go/test-fixtures/lint/src/vet/vet.go
Normal file
@ -0,0 +1,8 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
str := "hello world!"
|
||||
fmt.Printf("%d\n", str)
|
||||
}
|
||||
5
autoload/go/test-fixtures/term/term.go
Normal file
5
autoload/go/test-fixtures/term/term.go
Normal file
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello, world")
|
||||
}
|
||||
@ -18,6 +18,7 @@ func TestTopSubHelper(t *testing.T) {
|
||||
|
||||
func TestMultiline(t *testing.T) {
|
||||
t.Error("this is an error\nand a second line, too")
|
||||
t.Error("\nthis is another error")
|
||||
}
|
||||
|
||||
func TestSub(t *testing.T) {
|
||||
|
||||
11
autoload/go/test-fixtures/test/src/showname/showname_test.go
Normal file
11
autoload/go/test-fixtures/test/src/showname/showname_test.go
Normal file
@ -0,0 +1,11 @@
|
||||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestHelloWorld(t *testing.T) {
|
||||
t.Error("so long")
|
||||
|
||||
t.Run("sub", func(t *testing.T) {
|
||||
t.Error("thanks for all the fish")
|
||||
})
|
||||
}
|
||||
47
autoload/go/test-fixtures/test/src/timeout/timeout_test.go
Normal file
47
autoload/go/test-fixtures/test/src/timeout/timeout_test.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Run a few parallel tests, all in parallel, using multiple techniques for
|
||||
// causing the test to take a while so that the stacktraces resulting from a
|
||||
// test timeout will contain several goroutines to avoid giving a false sense
|
||||
// of confidence or creating error formats that don't account for the more
|
||||
// complex scenarios that can occur with timeouts.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSleep(t *testing.T) {
|
||||
t.Parallel()
|
||||
time.Sleep(15 * time.Second)
|
||||
t.Log("expected panic if run with timeout < 15s")
|
||||
}
|
||||
|
||||
func TestRunning(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := time.After(15 * time.Second)
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
break Loop
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
t.Log("expected panic if run with timeout < 15s")
|
||||
}
|
||||
|
||||
func TestRunningAlso(t *testing.T) {
|
||||
t.Parallel()
|
||||
c := time.After(15 * time.Second)
|
||||
Loop:
|
||||
for {
|
||||
select {
|
||||
case <-c:
|
||||
break Loop
|
||||
default:
|
||||
}
|
||||
}
|
||||
t.Log("expected panic if run with timeout < 15s")
|
||||
}
|
||||
@ -94,7 +94,6 @@ function! go#test#Test(bang, compile, ...) abort
|
||||
call go#util#EchoError("[test] FAIL")
|
||||
else
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
|
||||
if a:compile
|
||||
call go#util#EchoSuccess("[test] SUCCESS")
|
||||
@ -129,15 +128,16 @@ function! go#test#Func(bang, ...) abort
|
||||
|
||||
if a:0
|
||||
call extend(args, a:000)
|
||||
else
|
||||
" only add this if no custom flags are passed
|
||||
let timeout = get(g:, 'go_test_timeout', '10s')
|
||||
call add(args, printf("-timeout=%s", timeout))
|
||||
endif
|
||||
|
||||
call call('go#test#Test', args)
|
||||
endfunction
|
||||
|
||||
function! s:test_job(args) abort
|
||||
let status_dir = expand('%:p:h')
|
||||
let started_at = reltime()
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'current status',
|
||||
\ 'type': "test",
|
||||
@ -148,24 +148,37 @@ function! s:test_job(args) abort
|
||||
let status.state = "compiling"
|
||||
endif
|
||||
|
||||
call go#statusline#Update(status_dir, status)
|
||||
|
||||
" autowrite is not enabled for jobs
|
||||
call go#cmd#autowrite()
|
||||
|
||||
let messages = []
|
||||
function! s:callback(chan, msg) closure
|
||||
call add(messages, a:msg)
|
||||
let state = {
|
||||
\ 'exited': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'exitval': 0,
|
||||
\ 'messages': [],
|
||||
\ 'args': a:args,
|
||||
\ 'compile_test': a:args.compile_test,
|
||||
\ 'status_dir': expand('%:p:h'),
|
||||
\ 'started_at': reltime()
|
||||
\ }
|
||||
|
||||
call go#statusline#Update(state.status_dir, status)
|
||||
|
||||
function! s:callback(chan, msg) dict
|
||||
call add(self.messages, a:msg)
|
||||
endfunction
|
||||
|
||||
function! s:exit_cb(job, exitval) closure
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
let self.exitval = a:exitval
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
\ 'type': "test",
|
||||
\ 'state': "pass",
|
||||
\ }
|
||||
|
||||
if a:args.compile_test
|
||||
if self.compile_test
|
||||
let status.state = "success"
|
||||
endif
|
||||
|
||||
@ -175,7 +188,7 @@ function! s:test_job(args) abort
|
||||
|
||||
if get(g:, 'go_echo_command_info', 1)
|
||||
if a:exitval == 0
|
||||
if a:args.compile_test
|
||||
if self.compile_test
|
||||
call go#util#EchoSuccess("[test] SUCCESS")
|
||||
else
|
||||
call go#util#EchoSuccess("[test] PASS")
|
||||
@ -185,29 +198,33 @@ function! s:test_job(args) abort
|
||||
endif
|
||||
endif
|
||||
|
||||
let elapsed_time = reltimestr(reltime(started_at))
|
||||
let elapsed_time = reltimestr(reltime(self.started_at))
|
||||
" strip whitespace
|
||||
let elapsed_time = substitute(elapsed_time, '^\s*\(.\{-}\)\s*$', '\1', '')
|
||||
let status.state .= printf(" (%ss)", elapsed_time)
|
||||
|
||||
call go#statusline#Update(status_dir, status)
|
||||
call go#statusline#Update(self.status_dir, status)
|
||||
|
||||
let l:listtype = go#list#Type("GoTest")
|
||||
if a:exitval == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
return
|
||||
if self.closed
|
||||
call s:show_errors(self.args, self.exitval, self.messages)
|
||||
endif
|
||||
|
||||
" TODO(bc): When messages is JSON, the JSON should be run through a
|
||||
" filter to produce lines that are more easily described by errorformat.
|
||||
call s:show_errors(a:args, a:exitval, messages)
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if self.exited
|
||||
call s:show_errors(self.args, self.exitval, self.messages)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
" explicitly bind the callbacks to state so that self within them always
|
||||
" refers to state. See :help Partial for more information.
|
||||
let start_options = {
|
||||
\ 'callback': funcref("s:callback"),
|
||||
\ 'exit_cb': funcref("s:exit_cb"),
|
||||
\ }
|
||||
\ 'callback': funcref("s:callback", [], state),
|
||||
\ 'exit_cb': funcref("s:exit_cb", [], state),
|
||||
\ 'close_cb': funcref("s:close_cb", [], state)
|
||||
\ }
|
||||
|
||||
" pre start
|
||||
let dir = getcwd()
|
||||
@ -225,6 +242,15 @@ endfunction
|
||||
" a quickfix compatible list of errors. It's intended to be used only for go
|
||||
" test output.
|
||||
function! s:show_errors(args, exit_val, messages) abort
|
||||
let l:listtype = go#list#Type("GoTest")
|
||||
if a:exit_val == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
" TODO(bc): When messages is JSON, the JSON should be run through a
|
||||
" filter to produce lines that are more easily described by errorformat.
|
||||
|
||||
let l:listtype = go#list#Type("GoTest")
|
||||
|
||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||
@ -253,6 +279,7 @@ endfunction
|
||||
|
||||
|
||||
let s:efm= ""
|
||||
let s:go_test_show_name=0
|
||||
|
||||
function! s:errorformat() abort
|
||||
" NOTE(arslan): once we get JSON output everything will be easier :).
|
||||
@ -261,14 +288,16 @@ function! s:errorformat() abort
|
||||
" https://github.com/golang/go/issues/2981.
|
||||
let goroot = go#util#goroot()
|
||||
|
||||
if s:efm != ""
|
||||
let show_name=get(g:, 'go_test_show_name', 0)
|
||||
if s:efm != "" && s:go_test_show_name == show_name
|
||||
return s:efm
|
||||
endif
|
||||
let s:go_test_show_name = show_name
|
||||
|
||||
" each level of test indents the test output 4 spaces.
|
||||
" TODO(bc): figure out how to use 0 or more groups of four spaces for the
|
||||
" indentation. '%\\( %\\)%#' should work, but doesn't.
|
||||
let indent = " %#"
|
||||
" each level of test indents the test output 4 spaces. Capturing groups
|
||||
" (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
|
||||
" (e.g. \%(\)).
|
||||
let indent = '%\\%( %\\)%#'
|
||||
|
||||
" match compiler errors
|
||||
let format = "%f:%l:%c: %m"
|
||||
@ -285,8 +314,8 @@ function! s:errorformat() abort
|
||||
"
|
||||
" e.g.:
|
||||
" '--- FAIL: TestSomething (0.00s)'
|
||||
if get(g:, 'go_test_show_name', 0)
|
||||
let format .= ",%+G" . indent . "--- FAIL: %.%#"
|
||||
if show_name
|
||||
let format .= ",%G" . indent . "--- FAIL: %m (%.%#)"
|
||||
else
|
||||
let format .= ",%-G" . indent . "--- FAIL: %.%#"
|
||||
endif
|
||||
@ -298,6 +327,12 @@ function! s:errorformat() abort
|
||||
" message. e.g.:
|
||||
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
|
||||
let format .= ",%A" . indent . "%\\t%\\+%f:%l: %m"
|
||||
" also match lines that don't have a message (i.e. the message begins with a
|
||||
" newline or is the empty string):
|
||||
" e.g.:
|
||||
" t.Errorf("\ngot %v; want %v", actual, expected)
|
||||
" t.Error("")
|
||||
let format .= ",%A" . indent . "%\\t%\\+%f:%l: "
|
||||
|
||||
" Match the 2nd and later lines of multi-line output. These lines are
|
||||
" indented the number of spaces for the level of nesting of the test,
|
||||
@ -314,6 +349,10 @@ function! s:errorformat() abort
|
||||
|
||||
" set the format for panics.
|
||||
|
||||
" handle panics from test timeouts
|
||||
let format .= ",%+Gpanic: test timed out after %.%\\+"
|
||||
|
||||
" handle non-timeout panics
|
||||
" In addition to 'panic', check for 'fatal error' to support older versions
|
||||
" of Go that used 'fatal error'.
|
||||
"
|
||||
|
||||
@ -6,18 +6,19 @@ func! Test_GoTest() abort
|
||||
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
|
||||
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
|
||||
\ {'lnum': 25, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
|
||||
\ {'lnum': 21, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ''},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is another error'},
|
||||
\ {'lnum': 26, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
|
||||
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
|
||||
\ {'lnum': 42, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
|
||||
\ {'lnum': 43, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
|
||||
\ ]
|
||||
call s:test('play/play_test.go', expected)
|
||||
|
||||
endfunc
|
||||
|
||||
func! Test_GoTestConcurrentPanic()
|
||||
let expected = [
|
||||
\ {'lnum': 49, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: concurrent fail'}
|
||||
\ {'lnum': 50, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: concurrent fail'}
|
||||
\ ]
|
||||
call s:test('play/play_test.go', expected, "-run", "TestConcurrentPanic")
|
||||
endfunc
|
||||
@ -30,11 +31,13 @@ func! Test_GoTestVerbose() abort
|
||||
\ {'lnum': 16, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'helper badness'},
|
||||
\ {'lnum': 20, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is an error'},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
|
||||
\ {'lnum': 25, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
|
||||
\ {'lnum': 21, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': ''},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is another error'},
|
||||
\ {'lnum': 26, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'this is a sub-test error'},
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'and a second line, too'},
|
||||
\ {'lnum': 31, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'goodness'},
|
||||
\ {'lnum': 32, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'goodness'},
|
||||
\ {'lnum': 6, 'bufnr': 3, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'another package badness'},
|
||||
\ {'lnum': 42, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
|
||||
\ {'lnum': 43, 'bufnr': 2, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: worst ever [recovered]'}
|
||||
\ ]
|
||||
call s:test('play/play_test.go', expected, "-v")
|
||||
endfunc
|
||||
@ -43,10 +46,32 @@ func! Test_GoTestCompilerError() abort
|
||||
let expected = [
|
||||
\ {'lnum': 6, 'bufnr': 6, 'col': 22, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'syntax error: unexpected newline, expecting comma or )'}
|
||||
\ ]
|
||||
|
||||
call s:test('compilerror/compilerror_test.go', expected)
|
||||
endfunc
|
||||
|
||||
func! Test_GoTestTimeout() abort
|
||||
let expected = [
|
||||
\ {'lnum': 0, 'bufnr': 0, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'panic: test timed out after 500ms'}
|
||||
\ ]
|
||||
|
||||
let g:go_test_timeout="500ms"
|
||||
call s:test('timeout/timeout_test.go', expected)
|
||||
unlet g:go_test_timeout
|
||||
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': 9, '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': 9, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'thanks for all the fish'},
|
||||
\ ]
|
||||
|
||||
let g:go_test_show_name=1
|
||||
call s:test('showname/showname_test.go', expected)
|
||||
let g:go_test_show_name=0
|
||||
endfunc
|
||||
|
||||
func! s:test(file, expected, ...) abort
|
||||
if has('nvim')
|
||||
" nvim mostly shows test errors correctly, but the the expected errors are
|
||||
@ -57,7 +82,7 @@ func! s:test(file, expected, ...) abort
|
||||
" the tests will run for Neovim, too.
|
||||
return
|
||||
endif
|
||||
let $GOPATH = fnameescape(expand("%:p:h")) . '/test-fixtures/test'
|
||||
let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test'
|
||||
silent exe 'e ' . $GOPATH . '/src/' . a:file
|
||||
|
||||
" clear the quickfix lists
|
||||
@ -78,42 +103,15 @@ func! s:test(file, expected, ...) abort
|
||||
let actual = getqflist()
|
||||
endwhile
|
||||
|
||||
" for some reason, when run headless, the quickfix lists includes a line
|
||||
" that should have been filtered out; remove it manually. The line is not
|
||||
" present when run manually.
|
||||
let i = 0
|
||||
while i < len(actual)
|
||||
if actual[i].text =~# '^=== RUN .*'
|
||||
call remove(actual, i)
|
||||
endif
|
||||
let i += 1
|
||||
endwhile
|
||||
for item in actual
|
||||
let item.text = s:normalize_durations(item.text)
|
||||
endfor
|
||||
|
||||
call assert_equal(len(a:expected), len(actual), "number of errors")
|
||||
if len(a:expected) != len(actual)
|
||||
return
|
||||
endif
|
||||
for item in a:expected
|
||||
let item.text = s:normalize_durations(item.text)
|
||||
endfor
|
||||
|
||||
let i = 0
|
||||
while i < len(a:expected)
|
||||
let expected_item = a:expected[i]
|
||||
let actual_item = actual[i]
|
||||
let i += 1
|
||||
|
||||
call assert_equal(expected_item.bufnr, actual_item.bufnr, "bufnr")
|
||||
call assert_equal(expected_item.lnum, actual_item.lnum, "lnum")
|
||||
call assert_equal(expected_item.col, actual_item.col, "col")
|
||||
call assert_equal(expected_item.vcol, actual_item.vcol, "vcol")
|
||||
call assert_equal(expected_item.nr, actual_item.nr, "nr")
|
||||
call assert_equal(expected_item.pattern, actual_item.pattern, "pattern")
|
||||
|
||||
let expected_text = s:normalize_durations(expected_item.text)
|
||||
let actual_text = s:normalize_durations(actual_item.text)
|
||||
|
||||
call assert_equal(expected_text, actual_text, "text")
|
||||
call assert_equal(expected_item.type, actual_item.type, "type")
|
||||
call assert_equal(expected_item.valid, actual_item.valid, "valid")
|
||||
endwhile
|
||||
call gotest#assert_quickfix(actual, a:expected)
|
||||
endfunc
|
||||
|
||||
func! s:normalize_durations(str) abort
|
||||
|
||||
@ -17,6 +17,7 @@ endif
|
||||
" < >
|
||||
" t for tag
|
||||
|
||||
" Select a function in visual mode.
|
||||
function! go#textobj#Function(mode) abort
|
||||
let offset = go#util#OffsetCursor()
|
||||
|
||||
@ -98,23 +99,8 @@ function! go#textobj#Function(mode) abort
|
||||
call cursor(info.rbrace.line-1, 1)
|
||||
endfunction
|
||||
|
||||
function! go#textobj#FunctionJump(mode, direction) abort
|
||||
" get count of the motion. This should be done before all the normal
|
||||
" expressions below as those reset this value(because they have zero
|
||||
" count!). We abstract -1 because the index starts from 0 in motion.
|
||||
let l:cnt = v:count1 - 1
|
||||
|
||||
" set context mark so we can jump back with '' or ``
|
||||
normal! m'
|
||||
|
||||
" select already previously selected visual content and continue from there.
|
||||
" If it's the first time starts with the visual mode. This is needed so
|
||||
" after selecting something in visual mode, every consecutive motion
|
||||
" continues.
|
||||
if a:mode == 'v'
|
||||
normal! gv
|
||||
endif
|
||||
|
||||
" Get the location of the previous or next function.
|
||||
function! go#textobj#FunctionLocation(direction, cnt) abort
|
||||
let offset = go#util#OffsetCursor()
|
||||
|
||||
let fname = shellescape(expand("%:p"))
|
||||
@ -131,7 +117,7 @@ function! go#textobj#FunctionJump(mode, direction) abort
|
||||
endif
|
||||
|
||||
let command = printf("%s -format vim -file %s -offset %s", bin_path, fname, offset)
|
||||
let command .= ' -shift ' . l:cnt
|
||||
let command .= ' -shift ' . a:cnt
|
||||
|
||||
if a:direction == 'next'
|
||||
let command .= ' -mode next'
|
||||
@ -154,9 +140,33 @@ function! go#textobj#FunctionJump(mode, direction) abort
|
||||
call delete(l:tmpname)
|
||||
endif
|
||||
|
||||
" convert our string dict representation into native Vim dictionary type
|
||||
let result = eval(out)
|
||||
if type(result) != 4 || !has_key(result, 'fn')
|
||||
let l:result = json_decode(out)
|
||||
if type(l:result) != 4 || !has_key(l:result, 'fn')
|
||||
return 0
|
||||
endif
|
||||
|
||||
return l:result
|
||||
endfunction
|
||||
|
||||
function! go#textobj#FunctionJump(mode, direction) abort
|
||||
" get count of the motion. This should be done before all the normal
|
||||
" expressions below as those reset this value(because they have zero
|
||||
" count!). We abstract -1 because the index starts from 0 in motion.
|
||||
let l:cnt = v:count1 - 1
|
||||
|
||||
" set context mark so we can jump back with '' or ``
|
||||
normal! m'
|
||||
|
||||
" select already previously selected visual content and continue from there.
|
||||
" If it's the first time starts with the visual mode. This is needed so
|
||||
" after selecting something in visual mode, every consecutive motion
|
||||
" continues.
|
||||
if a:mode == 'v'
|
||||
normal! gv
|
||||
endif
|
||||
|
||||
let l:result = go#textobj#FunctionLocation(a:direction, l:cnt)
|
||||
if l:result is 0
|
||||
return
|
||||
endif
|
||||
|
||||
|
||||
@ -330,6 +330,7 @@ function! go#util#EchoWarning(msg)
|
||||
call s:echo(a:msg, 'WarningMsg')
|
||||
endfunction
|
||||
function! go#util#EchoProgress(msg)
|
||||
redraw
|
||||
call s:echo(a:msg, 'Identifier')
|
||||
endfunction
|
||||
function! go#util#EchoInfo(msg)
|
||||
@ -362,7 +363,6 @@ function! go#util#archive()
|
||||
return expand("%:p:gs!\\!/!") . "\n" . strlen(l:buffer) . "\n" . l:buffer
|
||||
endfunction
|
||||
|
||||
|
||||
" Make a named temporary directory which starts with "prefix".
|
||||
"
|
||||
" Unfortunately Vim's tempname() is not portable enough across various systems;
|
||||
@ -384,7 +384,7 @@ function! go#util#tempdir(prefix) abort
|
||||
endfor
|
||||
|
||||
if l:dir == ''
|
||||
echoerr 'Unable to find directory to store temporary directory in'
|
||||
call go#util#EchoError('Unable to find directory to store temporary directory in')
|
||||
return
|
||||
endif
|
||||
|
||||
@ -395,4 +395,9 @@ function! go#util#tempdir(prefix) abort
|
||||
return l:tmp
|
||||
endfunction
|
||||
|
||||
" Report if the user enabled a debug flag in g:go_debug.
|
||||
function! go#util#HasDebug(flag)
|
||||
return index(get(g:, 'go_debug', []), a:flag) >= 0
|
||||
endfunction
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
@ -102,4 +102,29 @@ fun! gotest#assert_fixture(path) abort
|
||||
call gotest#assert_buffer(0, l:want)
|
||||
endfun
|
||||
|
||||
func! gotest#assert_quickfix(got, want) abort
|
||||
call assert_equal(len(a:want), len(a:got), "number of errors")
|
||||
if len(a:want) != len(a:got)
|
||||
call assert_equal(a:want, a:got)
|
||||
return
|
||||
endif
|
||||
|
||||
let i = 0
|
||||
while i < len(a:want)
|
||||
let want_item = a:want[i]
|
||||
let got_item = a:got[i]
|
||||
let i += 1
|
||||
|
||||
call assert_equal(want_item.bufnr, got_item.bufnr, "bufnr")
|
||||
call assert_equal(want_item.lnum, got_item.lnum, "lnum")
|
||||
call assert_equal(want_item.col, got_item.col, "col")
|
||||
call assert_equal(want_item.vcol, got_item.vcol, "vcol")
|
||||
call assert_equal(want_item.nr, got_item.nr, "nr")
|
||||
call assert_equal(want_item.pattern, got_item.pattern, "pattern")
|
||||
call assert_equal(want_item.text, got_item.text, "text")
|
||||
call assert_equal(want_item.type, got_item.type, "type")
|
||||
call assert_equal(want_item.valid, got_item.valid, "valid")
|
||||
endwhile
|
||||
endfunc
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
273
doc/vim-go.txt
273
doc/vim-go.txt
@ -22,10 +22,11 @@ CONTENTS *go-contents*
|
||||
6. Functions....................................|go-functions|
|
||||
7. Settings.....................................|go-settings|
|
||||
8. Syntax highlighting..........................|go-syntax|
|
||||
9. FAQ/Troubleshooting..........................|go-troubleshooting|
|
||||
10. Development..................................|go-development|
|
||||
11. Donation.....................................|go-donation|
|
||||
12. Credits......................................|go-credits|
|
||||
9. Debugger.....................................|go-debug|
|
||||
10. FAQ/Troubleshooting..........................|go-troubleshooting|
|
||||
11. Development..................................|go-development|
|
||||
12. Donation.....................................|go-donation|
|
||||
13. Credits......................................|go-credits|
|
||||
|
||||
==============================================================================
|
||||
INTRO *go-intro*
|
||||
@ -40,13 +41,13 @@ tools developed by the Go community to provide a seamless Vim experience.
|
||||
test it with |:GoTest|. Run a single tests with |:GoTestFunc|).
|
||||
* Quickly execute your current file(s) with |:GoRun|.
|
||||
* Improved syntax highlighting and folding.
|
||||
* Debug programs with integrated `delve` support with |:GoDebugStart|.
|
||||
* Completion support via `gocode`.
|
||||
* `gofmt` or `goimports` on save keeps the cursor position and undo history.
|
||||
* Go to symbol/declaration with |:GoDef|.
|
||||
* Look up documentation with |:GoDoc| or |:GoDocBrowser|.
|
||||
* Easily import packages via |:GoImport|, remove them via |:GoDrop|.
|
||||
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
|
||||
display `GOPATH` with |:GoPath|.
|
||||
* Precise type-safe renaming of identifiers with |:GoRename|.
|
||||
* See which code is covered by tests with |:GoCoverage|.
|
||||
* Add or remove tags on struct fields with |:GoAddTags| and |:GoRemoveTags|.
|
||||
* Call `gometalinter` with |:GoMetaLinter| to invoke all possible linters
|
||||
@ -56,7 +57,8 @@ tools developed by the Go community to provide a seamless Vim experience.
|
||||
static errors, or make sure errors are checked with |:GoErrCheck|.
|
||||
* Advanced source analysis tools utilizing `guru`, such as |:GoImplements|,
|
||||
|:GoCallees|, and |:GoReferrers|.
|
||||
* Precise type-safe renaming of identifiers with |:GoRename|.
|
||||
* Automatic `GOPATH` detection which works with `gb` and `godep`. Change or
|
||||
display `GOPATH` with |:GoPath|.
|
||||
* Integrated and improved snippets, supporting `ultisnips`, `neosnippet`,
|
||||
and `vim-minisnip`.
|
||||
* Share your current code to play.golang.org with |:GoPlay|.
|
||||
@ -797,6 +799,9 @@ CTRL-t
|
||||
|
||||
Toggles |'g:go_metalinter_autosave'|.
|
||||
|
||||
By default, `gometalinter` messages will be shown in the |location-list|
|
||||
window. The list to use can be set using |'g:go_list_type_commands'|.
|
||||
|
||||
*:GoTemplateAutoCreateToggle*
|
||||
:GoTemplateAutoCreateToggle
|
||||
|
||||
@ -1097,7 +1102,7 @@ cleaned for each package after `60` seconds. This can be changed with the
|
||||
*go#complete#GetInfo()*
|
||||
|
||||
Returns the description of the identifer under the cursor. Can be used to plug
|
||||
into the statusline. This function is also used for |'g:go_auto_type_info'|.
|
||||
into the statusline.
|
||||
|
||||
==============================================================================
|
||||
SETTINGS *go-settings*
|
||||
@ -1250,7 +1255,7 @@ Maximum height for the GoDoc window created with |:GoDoc|. Default is 20. >
|
||||
*'g:go_doc_url'*
|
||||
|
||||
godoc server URL used when |:GoDocBrowser| is used. Change if you want to use
|
||||
a private internal service. Default is 'https://godoc.org'.
|
||||
a private internal service. Default is 'https://godoc.org'.
|
||||
>
|
||||
let g:go_doc_url = 'https://godoc.org'
|
||||
<
|
||||
@ -1371,8 +1376,12 @@ function when using the `af` text object. By default it's enabled. >
|
||||
*'g:go_metalinter_autosave'*
|
||||
|
||||
Use this option to auto |:GoMetaLinter| on save. Only linter messages for
|
||||
the active buffer will be shown. By default it's disabled >
|
||||
the active buffer will be shown.
|
||||
|
||||
By default, `gometalinter` messages will be shown in the |location-list|
|
||||
window. The list to use can be set using |'g:go_list_type_commands'|.
|
||||
|
||||
By default it's disabled >
|
||||
let g:go_metalinter_autosave = 0
|
||||
<
|
||||
*'g:go_metalinter_autosave_enabled'*
|
||||
@ -1384,17 +1393,17 @@ default it's using `vet` and `golint`.
|
||||
<
|
||||
*'g:go_metalinter_enabled'*
|
||||
|
||||
Specifies the currently enabled linters for the |:GoMetaLinter| command. By
|
||||
default it's using `vet`, `golint` and `errcheck`.
|
||||
Specifies the linters to enable for the |:GoMetaLinter| command. By default
|
||||
it's using `vet`, `golint` and `errcheck`.
|
||||
>
|
||||
let g:go_metalinter_enabled = ['vet', 'golint', 'errcheck']
|
||||
<
|
||||
*'g:go_metalinter_excludes'*
|
||||
*'g:go_metalinter_disabled'*
|
||||
|
||||
Specifies the linters to be excluded from the |:GoMetaLinter| command. By
|
||||
default it's empty
|
||||
Specifies the linters to disable for the |:GoMetaLinter| command. By default
|
||||
it's empty
|
||||
>
|
||||
let g:go_metalinter_excludes = []
|
||||
let g:go_metalinter_disabled = []
|
||||
<
|
||||
*'g:go_metalinter_command'*
|
||||
|
||||
@ -1437,9 +1446,9 @@ Specifies the type of list to use for command outputs (such as errors from
|
||||
builds, results from static analysis commands, etc...). When an expected key
|
||||
is not present in the dictionary, |'g:go_list_type'| will be used instead.
|
||||
Supported keys are "GoBuild", "GoErrCheck", "GoFmt", "GoInstall", "GoLint",
|
||||
"GoMetaLinter", "GoModifyTags" (used for both :GoAddTags and :GoRemoveTags),
|
||||
"GoRename", "GoRun", and "GoTest". Supported values for each command are
|
||||
"quickfix" and "locationlist".
|
||||
"GoMetaLinter", "GoMetaLinterAutoSave", "GoModifyTags" (used for both
|
||||
:GoAddTags and :GoRemoveTags), "GoRename", "GoRun", and "GoTest". Supported
|
||||
values for each command are "quickfix" and "locationlist".
|
||||
>
|
||||
let g:go_list_type_commands = {}
|
||||
<
|
||||
@ -1651,6 +1660,18 @@ By default "snakecase" is used. Current values are: ["snakecase",
|
||||
>
|
||||
let g:go_addtags_transform = 'snakecase'
|
||||
<
|
||||
*'g:go_debug'*
|
||||
|
||||
A list of options to debug; useful for development and/or reporting bugs.
|
||||
|
||||
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`.
|
||||
>
|
||||
let g:go_debug = []
|
||||
<
|
||||
|
||||
==============================================================================
|
||||
SYNTAX HIGHLIGHTING *ft-go-syntax* *go-syntax*
|
||||
@ -1725,7 +1746,7 @@ Highlight operators such as `:=` , `==`, `-=`, etc.
|
||||
<
|
||||
*'g:go_highlight_functions'*
|
||||
|
||||
Highlight function names.
|
||||
Highlight function and method declarations.
|
||||
>
|
||||
let g:go_highlight_functions = 0
|
||||
<
|
||||
@ -1737,11 +1758,11 @@ declarations. Setting this implies the functionality from
|
||||
>
|
||||
let g:go_highlight_function_arguments = 0
|
||||
<
|
||||
*'g:go_highlight_methods'*
|
||||
*'g:go_highlight_function_calls'*
|
||||
|
||||
Highlight method names.
|
||||
Highlight function and method calls.
|
||||
>
|
||||
let g:go_highlight_methods = 0
|
||||
let g:go_highlight_function_calls = 0
|
||||
<
|
||||
*'g:go_highlight_types'*
|
||||
|
||||
@ -1808,6 +1829,212 @@ The `gohtmltmpl` filetype is automatically set for `*.tmpl` files; the
|
||||
`gotexttmpl` is never automatically set and needs to be set manually.
|
||||
|
||||
|
||||
==============================================================================
|
||||
DEBUGGER *go-debug*
|
||||
|
||||
Vim-go comes with a special "debugger mode". This starts a `dlv` process in
|
||||
the background and provides various commands to communicate with it.
|
||||
|
||||
This debugger is similar to Visual Studio or Eclipse and has the following
|
||||
features:
|
||||
|
||||
* Show stack trace and jumps.
|
||||
* List local variables.
|
||||
* List function arguments.
|
||||
* Expand values of struct or array/slice.
|
||||
* Show balloon on the symbol.
|
||||
* Show output of stdout/stderr.
|
||||
* Toggle breakpoint.
|
||||
* Stack operation continue/next/step out.
|
||||
|
||||
This feature requires Vim 8.0.0087 or newer with the |+job| feature. Neovim
|
||||
does _not_ work (yet).
|
||||
This requires Delve 1.0.0 or newer, and it is recommended to use Go 1.10 or
|
||||
newer, as its new caching will speed up recompiles.
|
||||
|
||||
*go-debug-intro*
|
||||
GETTING STARTED WITH THE DEBUGGER~
|
||||
|
||||
Use |:GoDebugStart| or |:GoDebugTest| to start the debugger. The first
|
||||
argument is the package name, and any arguments after that will be passed on
|
||||
to the program; for example:
|
||||
>
|
||||
:GoDebugStart . -someflag value
|
||||
<
|
||||
This may take few seconds. After the code is compiled you'll see three new
|
||||
windows: the stack trace on left side, the variable list on the bottom-left,
|
||||
and program output at the bottom.
|
||||
|
||||
You can add breakpoints with |:GoDebugBreakpoint| (<F9>) and run your program
|
||||
with |:GoDebugContinue| (<F5>).
|
||||
|
||||
The program will halt on the breakpoint, at which point you can inspect the
|
||||
program state. You can go to the next line with |:GoDebugNext| (<F10>) or step
|
||||
in with |:GoDebugStep| (<F11>).
|
||||
|
||||
The variable window in the bottom left (`GODEBUG_VARIABLES`) will display all
|
||||
local variables. Struct values are displayed as `{...}`, array/slices as
|
||||
`[4]`. Use <CR> on the variable name to expand the values.
|
||||
|
||||
The `GODEBUG_OUTPUT` window displays output from the program and the Delve
|
||||
debugger.
|
||||
|
||||
The `GODEBUG_STACKTRACE` window can be used to jump to different places in the
|
||||
call stack.
|
||||
|
||||
When you're done use |:GoDebugStop| to close the debugging windows and halt
|
||||
the `dlv` process, or |:GoDebugRestart| to recompile the code.
|
||||
|
||||
*go-debug-commands*
|
||||
DEBUGGER COMMANDS~
|
||||
|
||||
Only |:GoDebugStart| and |:GoDebugBreakpoint| are available by default; the
|
||||
rest of the commands and mappings become available after starting debug mode.
|
||||
|
||||
*:GoDebugStart*
|
||||
:GoDebugStart [pkg] [program-args]
|
||||
|
||||
Start the debug mode for [pkg]; this does several things:
|
||||
|
||||
* Setup the debug windows according to |'g:go_debug_windows'|.
|
||||
* Make the `:GoDebug*` commands and `(go-debug-*)` mappings available.
|
||||
|
||||
The current directory is used if [pkg] is empty. Any other arguments will
|
||||
be passed to the program.
|
||||
|
||||
Use |:GoDebugStop| to stop `dlv` and exit debugging mode.
|
||||
|
||||
*:GoDebugTest*
|
||||
:GoDebugTest [pkg] [program-args]
|
||||
|
||||
Behaves the same as |:GoDebugStart| but runs `dlv test` instead of
|
||||
`dlv debug` so you can debug tests.
|
||||
|
||||
Use `-test.flag` to pass flags to `go test` when debugging a test; for
|
||||
example `-test.v` or `-test.run TestFoo`
|
||||
|
||||
|
||||
*:GoDebugRestart*
|
||||
:GoDebugRestart
|
||||
|
||||
Stop the program (if running) and restart `dlv` to recompile the package.
|
||||
The current window layout and breakpoints will be left intact.
|
||||
|
||||
*:GoDebugStop*
|
||||
*(go-debug-stop)*
|
||||
:GoDebugStop
|
||||
|
||||
Stop `dlv` and remove all debug-specific commands, mappings, and windows.
|
||||
|
||||
*:GoDebugBreakpoint*
|
||||
*(go-debug-breakpoint)*
|
||||
:GoDebugBreakpoint [linenr]
|
||||
|
||||
Toggle breakpoint for the [linenr]. [linenr] defaults to the current line
|
||||
if it is omitted. A line with a breakpoint will have the
|
||||
{godebugbreakpoint} |:sign| placed on it. The line the program is
|
||||
currently halted on will have the {godebugcurline} sign.
|
||||
|
||||
*hl-GoDebugCurrent* *hl-GoDebugBreakpoint*
|
||||
A line with a breakpoint will be highlighted with the {GoDebugBreakpoint}
|
||||
group; the line the program is currently halted on will be highlighted
|
||||
with {GoDebugCurrent}.
|
||||
|
||||
Mapped to <F9> by default.
|
||||
|
||||
*:GoDebugContinue*
|
||||
*(go-debug-continue)*
|
||||
:GoDebugContinue
|
||||
|
||||
Continue execution until breakpoint or program termination. It will start
|
||||
the program if it hasn't been started yet.
|
||||
|
||||
Mapped to <F5> by default.
|
||||
|
||||
*:GoDebugNext*
|
||||
*(go-debug-next)*
|
||||
:GoDebugNext
|
||||
|
||||
Advance execution by one line, also called "step over" by some other
|
||||
debuggers.
|
||||
It will behave as |:GoDebugContinue| if the program isn't started.
|
||||
|
||||
Mapped to <F10> by default.
|
||||
|
||||
*:GoDebugStep*
|
||||
*(go-debug-step)*
|
||||
:GoDebugStep
|
||||
|
||||
Advance execution by one step, stopping at the next line of code that will
|
||||
be executed (regardless of location).
|
||||
It will behave as |:GoDebugContinue| if the program isn't started.
|
||||
|
||||
Mapped to <F11> by default.
|
||||
|
||||
*:GoDebugStepOut*
|
||||
*(go-debug-stepout)*
|
||||
|
||||
:GoDebugStepOut
|
||||
|
||||
Run all the code in the current function and halt when the function
|
||||
returns ("step out of the current function").
|
||||
It will behave as |:GoDebugContinue| if the program isn't started.
|
||||
|
||||
*:GoDebugSet*
|
||||
:GoDebugSet {var} {value}
|
||||
|
||||
Set the variable {var} to {value}. Example:
|
||||
>
|
||||
:GoDebugSet truth 42
|
||||
<
|
||||
This only works for `float`, `int` and variants, `uint` and variants,
|
||||
`bool`, and pointers (this is a `delve` limitation, not a vim-go
|
||||
limitation).
|
||||
|
||||
*:GoDebugPrint*
|
||||
*(go-debug-print)*
|
||||
:GoDebugPrint {expr}
|
||||
|
||||
Print the result of a Go expression.
|
||||
>
|
||||
:GoDebugPrint truth == 42
|
||||
truth == 42 true
|
||||
<
|
||||
Mapped to <F6> by default, which will evaluate the <cword> under the
|
||||
cursor.
|
||||
|
||||
*go-debug-settings*
|
||||
DEBUGGER SETTINGS~
|
||||
|
||||
*'g:go_debug_windows'*
|
||||
|
||||
Controls the window layout for debugging mode. This is a |dict| with three
|
||||
possible keys: "stack", "out", and "vars"; the windows will created in that
|
||||
order with the commands in the value.
|
||||
A window will not be created if a key is missing or empty.
|
||||
|
||||
Defaults:
|
||||
>
|
||||
let g:go_debug_windows = {
|
||||
\ 'stack': 'leftabove 20vnew',
|
||||
\ 'out': 'botright 10new',
|
||||
\ 'vars': 'leftabove 30vnew',
|
||||
\ }
|
||||
<
|
||||
Show only variables on the right-hand side: >
|
||||
|
||||
let g:go_debug_windows = {
|
||||
\ 'vars': 'rightbelow 60vnew',
|
||||
\ }
|
||||
<
|
||||
*'g:go_debug_address'*
|
||||
|
||||
Server address `dlv` will listen on; must be in `hostname:port` format.
|
||||
Defaults to `127.0.0.1:8181`:
|
||||
>
|
||||
let g:go_debug_address = '127.0.0.1:8181'
|
||||
<
|
||||
|
||||
==============================================================================
|
||||
FAQ TROUBLESHOOTING *go-troubleshooting*
|
||||
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
" vint: -ProhibitAutocmdWithNoGroup
|
||||
|
||||
" We take care to preserve the user's fileencodings and fileformats,
|
||||
" because those settings are global (not buffer local), yet we want
|
||||
" to override them for loading Go files, which are defined to be UTF-8.
|
||||
@ -18,17 +20,15 @@ function! s:gofiletype_post()
|
||||
let &g:fileencodings = s:current_fileencodings
|
||||
endfunction
|
||||
|
||||
augroup vim-go-filetype
|
||||
autocmd!
|
||||
au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix
|
||||
au BufRead *.go call s:gofiletype_pre("go")
|
||||
au BufReadPost *.go call s:gofiletype_post()
|
||||
" Note: should not use augroup in ftdetect (see :help ftdetect)
|
||||
au BufNewFile *.go setfiletype go | setlocal fileencoding=utf-8 fileformat=unix
|
||||
au BufRead *.go call s:gofiletype_pre("go")
|
||||
au BufReadPost *.go call s:gofiletype_post()
|
||||
|
||||
au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix
|
||||
au BufRead *.s call s:gofiletype_pre("asm")
|
||||
au BufReadPost *.s call s:gofiletype_post()
|
||||
au BufNewFile *.s setfiletype asm | setlocal fileencoding=utf-8 fileformat=unix
|
||||
au BufRead *.s call s:gofiletype_pre("asm")
|
||||
au BufReadPost *.s call s:gofiletype_post()
|
||||
|
||||
au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl
|
||||
augroup end
|
||||
au BufRead,BufNewFile *.tmpl set filetype=gohtmltmpl
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
@ -98,4 +98,11 @@ command! -nargs=0 GoKeyify call go#keyify#Keyify()
|
||||
" -- fillstruct
|
||||
command! -nargs=0 GoFillStruct call go#fillstruct#FillStruct()
|
||||
|
||||
" -- debug
|
||||
if !exists(':GoDebugStart')
|
||||
command! -nargs=* -complete=customlist,go#package#Complete GoDebugStart call go#debug#Start(0, <f-args>)
|
||||
command! -nargs=* -complete=customlist,go#package#Complete GoDebugTest call go#debug#Start(1, <f-args>)
|
||||
command! -nargs=? GoDebugBreakpoint call go#debug#Breakpoint(<f-args>)
|
||||
endif
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
|
||||
@ -40,7 +40,7 @@ function! s:GoMinisnip() abort
|
||||
endif
|
||||
|
||||
if exists('g:minisnip_dir')
|
||||
let g:minisnip_dir .= ':' . globpath(&rtp, 'gosnippets/minisnip')
|
||||
let g:minisnip_dir .= go#util#PathListSep() . globpath(&rtp, 'gosnippets/minisnip')
|
||||
else
|
||||
let g:minisnip_dir = globpath(&rtp, 'gosnippets/minisnip')
|
||||
endif
|
||||
|
||||
@ -252,6 +252,11 @@ snippet fn "fmt.Println(...)"
|
||||
fmt.Println("${1:${VISUAL}}")
|
||||
endsnippet
|
||||
|
||||
# Fmt Errorf debug
|
||||
snippet fe "fmt.Errorf(...)"
|
||||
fmt.Errorf("${1:${VISUAL}}")
|
||||
endsnippet
|
||||
|
||||
# log printf
|
||||
snippet lf "log.Printf(...)"
|
||||
log.Printf("${1:${VISUAL}} = %+v\n", $1)
|
||||
|
||||
@ -128,21 +128,21 @@ abbr if err := ...; err != nil { ... }
|
||||
|
||||
# error snippet
|
||||
snippet errn
|
||||
abbr if err != nil { ... }
|
||||
abbr if err != nil { return err }
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
${0}
|
||||
# error snippet in TestFunc
|
||||
snippet errt
|
||||
abbr if err != nil { ... }
|
||||
abbr if err != nil { t.Fatal(err) }
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
# error snippet in log.Fatal
|
||||
snippet errl
|
||||
abbr if err != nil { ... }
|
||||
abbr if err != nil { log.Fatal(err) }
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@ -157,7 +157,7 @@ abbr if err != nil { return [...], err }
|
||||
|
||||
# error snippet handle and return
|
||||
snippet errh
|
||||
abbr if err != nil { return }
|
||||
abbr if err != nil { ... return }
|
||||
if err != nil {
|
||||
${1}
|
||||
return
|
||||
@ -166,7 +166,7 @@ abbr if err != nil { return }
|
||||
|
||||
# error snippet with panic
|
||||
snippet errp
|
||||
abbr if err != nil { ... }
|
||||
abbr if err != nil { panic(...) }
|
||||
if err != nil {
|
||||
panic(${1})
|
||||
}
|
||||
@ -219,6 +219,10 @@ abbr fmt.Printf(...)
|
||||
snippet fn
|
||||
abbr fmt.Println(...)
|
||||
fmt.Println("${1}")
|
||||
# Fmt Errorf
|
||||
snippet fe
|
||||
abbr fmt.Errorf(...)
|
||||
fmt.Errorf("${1}")
|
||||
# log printf
|
||||
snippet lf
|
||||
abbr log.Printf(...)
|
||||
|
||||
@ -31,6 +31,7 @@ endif
|
||||
" needed by the user with GoInstallBinaries
|
||||
let s:packages = {
|
||||
\ 'asmfmt': ['github.com/klauspost/asmfmt/cmd/asmfmt'],
|
||||
\ 'dlv': ['github.com/derekparker/delve/cmd/dlv'],
|
||||
\ 'errcheck': ['github.com/kisielk/errcheck'],
|
||||
\ 'fillstruct': ['github.com/davidrjenni/reftools/cmd/fillstruct'],
|
||||
\ 'gocode': ['github.com/nsf/gocode', {'windows': '-ldflags -H=windowsgui'}],
|
||||
|
||||
@ -42,8 +42,8 @@ if !exists("g:go_highlight_function_arguments")
|
||||
let g:go_highlight_function_arguments = 0
|
||||
endif
|
||||
|
||||
if !exists("g:go_highlight_methods")
|
||||
let g:go_highlight_methods = 0
|
||||
if !exists("g:go_highlight_function_calls")
|
||||
let g:go_highlight_function_calls = 0
|
||||
endif
|
||||
|
||||
if !exists("g:go_highlight_fields")
|
||||
@ -202,7 +202,19 @@ else
|
||||
endif
|
||||
|
||||
if g:go_highlight_format_strings != 0
|
||||
syn match goFormatSpecifier /\([^%]\(%%\)*\)\@<=%[-#0 +]*\%(\*\|\d\+\)\=\%(\.\%(\*\|\d\+\)\)*[vTtbcdoqxXUeEfgGsp]/ contained containedin=goString
|
||||
" [n] notation is valid for specifying explicit argument indexes
|
||||
" 1. Match a literal % not preceded by a %.
|
||||
" 2. Match any number of -, #, 0, space, or +
|
||||
" 3. Match * or [n]* or any number or nothing before a .
|
||||
" 4. Match * or [n]* or any number or nothing after a .
|
||||
" 5. Match [n] or nothing before a verb
|
||||
" 6. Match a formatting verb
|
||||
syn match goFormatSpecifier /\
|
||||
\([^%]\(%%\)*\)\
|
||||
\@<=%[-#0 +]*\
|
||||
\%(\%(\%(\[\d\+\]\)\=\*\)\|\d\+\)\=\
|
||||
\%(\.\%(\%(\%(\[\d\+\]\)\=\*\)\|\d\+\)\=\)\=\
|
||||
\%(\[\d\+\]\)\=[vTtbcdoqxXUeEfFgGsp]/ contained containedin=goString,goRawString
|
||||
hi def link goFormatSpecifier goSpecialString
|
||||
endif
|
||||
|
||||
@ -348,7 +360,6 @@ hi def link goOperator Operator
|
||||
|
||||
" Functions;
|
||||
if g:go_highlight_functions isnot 0 || g:go_highlight_function_arguments isnot 0
|
||||
syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration
|
||||
syn match goDeclaration /\<func\>/ nextgroup=goReceiver,goFunction,goSimpleArguments skipwhite skipnl
|
||||
syn match goReceiverVar /\w\+\ze\s\+\(\w\|\*\)/ nextgroup=goPointerOperator,goReceiverType skipwhite skipnl contained
|
||||
syn match goPointerOperator /\*/ nextgroup=goReceiverType contained skipwhite skipnl
|
||||
@ -367,13 +378,12 @@ else
|
||||
syn keyword goDeclaration func
|
||||
endif
|
||||
hi def link goFunction Function
|
||||
hi def link goFunctionCall Type
|
||||
|
||||
" Methods;
|
||||
if g:go_highlight_methods != 0
|
||||
syn match goMethodCall /\.\w\+\ze(/hs=s+1
|
||||
" Function calls;
|
||||
if g:go_highlight_function_calls != 0
|
||||
syn match goFunctionCall /\w\+\ze(/ contains=goBuiltins,goDeclaration
|
||||
endif
|
||||
hi def link goMethodCall Type
|
||||
hi def link goFunctionCall Type
|
||||
|
||||
" Fields;
|
||||
if g:go_highlight_fields != 0
|
||||
@ -443,7 +453,7 @@ if g:go_highlight_build_constraints != 0 || s:fold_package_comment
|
||||
\ . ' contains=@goCommentGroup,@Spell'
|
||||
\ . (s:fold_package_comment ? ' fold' : '')
|
||||
exe 'syn region goPackageComment start=/\v\/\*.*\n(.*\n)*\s*\*\/\npackage/'
|
||||
\ . ' end=/\v\n\s*package/he=e-7,me=e-7,re=e-7'
|
||||
\ . ' end=/\v\*\/\n\s*package/he=e-7,me=e-7,re=e-7'
|
||||
\ . ' contains=@goCommentGroup,@Spell'
|
||||
\ . (s:fold_package_comment ? ' fold' : '')
|
||||
hi def link goPackageComment Comment
|
||||
|
||||
13
syntax/godebugoutput.vim
Normal file
13
syntax/godebugoutput.vim
Normal file
@ -0,0 +1,13 @@
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn match godebugOutputErr '^ERR:.*'
|
||||
syn match godebugOutputOut '^OUT:.*'
|
||||
|
||||
let b:current_syntax = "godebugoutput"
|
||||
|
||||
hi def link godebugOutputErr Comment
|
||||
hi def link godebugOutputOut Normal
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
11
syntax/godebugstacktrace.vim
Normal file
11
syntax/godebugstacktrace.vim
Normal file
@ -0,0 +1,11 @@
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn match godebugStacktrace '^\S\+'
|
||||
|
||||
let b:current_syntax = "godebugoutput"
|
||||
|
||||
hi def link godebugStacktrace SpecialKey
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
23
syntax/godebugvariables.vim
Normal file
23
syntax/godebugvariables.vim
Normal file
@ -0,0 +1,23 @@
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
syn match godebugTitle '^#.*'
|
||||
syn match godebugVariables '^\s*\S\+\ze:'
|
||||
|
||||
syn keyword goType chan map bool string error
|
||||
syn keyword goSignedInts int int8 int16 int32 int64 rune
|
||||
syn keyword goUnsignedInts byte uint uint8 uint16 uint32 uint64 uintptr
|
||||
syn keyword goFloats float32 float64
|
||||
syn keyword goComplexes complex64 complex128
|
||||
|
||||
syn keyword goBoolean true false
|
||||
|
||||
let b:current_syntax = "godebugvariables"
|
||||
|
||||
hi def link godebugTitle Underlined
|
||||
hi def link godebugVariables Statement
|
||||
hi def link goType Type
|
||||
hi def link goBoolean Boolean
|
||||
|
||||
" vim: sw=2 ts=2 et
|
||||
Loading…
Reference in New Issue
Block a user