Merge commit '2aa623055499f7996782cbf6a20ad1ab0abe6c1b' into main
This commit is contained in:
commit
4a7f7bd422
12
vim/bundle/go/.codecov.yml
Normal file
12
vim/bundle/go/.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$"
|
||||
@ -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
vim/bundle/go/autoload/go/cmd_test.vim
Normal file
30
vim/bundle/go/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
vim/bundle/go/autoload/go/debug.vim
Normal file
904
vim/bundle/go/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
vim/bundle/go/autoload/go/lint_test.vim
Normal file
131
vim/bundle/go/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
vim/bundle/go/autoload/go/term_test.vim
Normal file
50
vim/bundle/go/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
vim/bundle/go/autoload/go/test-fixtures/cmd/bad.go
Normal file
5
vim/bundle/go/autoload/go/test-fixtures/cmd/bad.go
Normal file
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
notafunc()
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package foo
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MissingFooDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package lint
|
||||
|
||||
import "fmt"
|
||||
|
||||
func MissingDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package lint
|
||||
|
||||
import "fmt"
|
||||
|
||||
func AlsoMissingDoc() {
|
||||
fmt.Println("missing doc")
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
str := "hello world!"
|
||||
fmt.Printf("%d\n", str)
|
||||
}
|
||||
5
vim/bundle/go/autoload/go/test-fixtures/term/term.go
Normal file
5
vim/bundle/go/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) {
|
||||
|
||||
@ -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")
|
||||
})
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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
vim/bundle/go/syntax/godebugoutput.vim
Normal file
13
vim/bundle/go/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
vim/bundle/go/syntax/godebugstacktrace.vim
Normal file
11
vim/bundle/go/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
vim/bundle/go/syntax/godebugvariables.vim
Normal file
23
vim/bundle/go/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