mirror of
https://github.com/amix/vimrc
synced 2025-06-16 01:25:00 +08:00
Updated plugins
This commit is contained in:
@ -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()
|
||||
|
30
sources_non_forked/vim-go/autoload/go/cmd_test.vim
Normal file
30
sources_non_forked/vim-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,7 +45,7 @@ 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()
|
||||
|
904
sources_non_forked/vim-go/autoload/go/debug.vim
Normal file
904
sources_non_forked/vim-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
|
@ -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,60 +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
|
||||
let closed = 0
|
||||
let exited = 0
|
||||
|
||||
function! s:exit_cb(job, exitval) closure
|
||||
let exited = 1
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
|
||||
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)
|
||||
call go#statusline#Update(self.status_dir, status)
|
||||
|
||||
if closed
|
||||
call s:complete()
|
||||
if self.closed
|
||||
call self.complete()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) closure
|
||||
let closed = 1
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if exited
|
||||
call s:complete()
|
||||
if self.exited
|
||||
call self.complete()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:complete() closure
|
||||
let out = join(messages, "\n")
|
||||
function state.complete() dict
|
||||
let out = join(self.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)
|
||||
endif
|
||||
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()
|
||||
@ -217,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)
|
||||
@ -289,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
|
||||
@ -464,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
|
||||
@ -495,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] !=# '{'
|
||||
|
@ -19,10 +19,6 @@
|
||||
" 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.
|
||||
" 'callback':
|
||||
" A function to call when there is a message to read from the job's
|
||||
" channel. The function will be passed two arguments: the channel and a
|
||||
" message. See job-callback.
|
||||
|
||||
" The return value is a dictionary with these keys:
|
||||
" 'callback':
|
||||
@ -36,43 +32,46 @@
|
||||
" job-close_cb.
|
||||
function go#job#Spawn(args)
|
||||
let cbs = {}
|
||||
|
||||
let winnr = winnr()
|
||||
let dir = getcwd()
|
||||
let jobdir = fnameescape(expand("%:p:h"))
|
||||
let messages = []
|
||||
let args = a:args.cmd
|
||||
let bang = 0
|
||||
let for = "_job"
|
||||
let state = {
|
||||
\ 'winnr': winnr(),
|
||||
\ 'dir': getcwd(),
|
||||
\ 'jobdir': fnameescape(expand("%:p:h")),
|
||||
\ 'messages': [],
|
||||
\ 'args': a:args.cmd,
|
||||
\ 'bang': 0,
|
||||
\ 'for': "_job",
|
||||
\ 'exited': 0,
|
||||
\ 'exit_status': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'errorformat': &errorformat
|
||||
\ }
|
||||
|
||||
if has_key(a:args, 'bang')
|
||||
let l:bang = a:args.bang
|
||||
let state.bang = a:args.bang
|
||||
endif
|
||||
|
||||
if has_key(a:args, 'for')
|
||||
let l:for = a:args.for
|
||||
let state.for = a:args.for
|
||||
endif
|
||||
|
||||
let l:exited = 0
|
||||
let l:exit_status = 0
|
||||
let l:closed = 0
|
||||
|
||||
function! s:NopComplete(job, exit_status, data)
|
||||
" do nothing in state.complete by default.
|
||||
function state.complete(job, exit_status, data)
|
||||
endfunction
|
||||
|
||||
let Complete = funcref('s:NopComplete')
|
||||
|
||||
if has_key(a:args, 'complete')
|
||||
let Complete = a:args.complete
|
||||
let state.complete = a:args.complete
|
||||
endif
|
||||
|
||||
function cbs.callback(chan, msg) dict closure
|
||||
call add(messages, a:msg)
|
||||
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 cbs.exit_cb(job, exitval) dict closure
|
||||
let exit_status = a:exitval
|
||||
let exited = 1
|
||||
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
|
||||
@ -82,56 +81,63 @@ function go#job#Spawn(args)
|
||||
endif
|
||||
endif
|
||||
|
||||
if closed
|
||||
call Complete(a:job, exit_status, messages)
|
||||
call s:show_errors(a:job, exit_status, 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 cbs.close_cb(ch) dict closure
|
||||
let closed = 1
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if exited
|
||||
if self.exited
|
||||
let job = ch_getjob(a:ch)
|
||||
call Complete(job, exit_status, messages)
|
||||
call s:show_errors(job, exit_status, messages)
|
||||
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! s:show_errors(job, exit_status, data) closure
|
||||
let l:listtype = go#list#Type(for)
|
||||
function state.show_errors(job, exit_status, data)
|
||||
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)
|
||||
return
|
||||
endif
|
||||
|
||||
let l:listtype = go#list#Type(for)
|
||||
let l:listtype = go#list#Type(self.for)
|
||||
if len(a:data) == 0
|
||||
call go#list#Clean(l:listtype)
|
||||
call go#list#Window(l:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
let out = join(self.messages, "\n")
|
||||
|
||||
let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd '
|
||||
try
|
||||
execute cd jobdir
|
||||
let errors = go#tool#ParseErrors(a:data)
|
||||
let errors = go#tool#FilterValids(errors)
|
||||
" parse the errors relative to self.jobdir
|
||||
execute cd self.jobdir
|
||||
call go#list#ParseFormat(l:listtype, self.errorformat, out, self.for)
|
||||
let errors = go#list#Get(l:listtype)
|
||||
finally
|
||||
execute cd . fnameescape(dir)
|
||||
execute cd . fnameescape(self.dir)
|
||||
endtry
|
||||
|
||||
|
||||
if empty(errors)
|
||||
" failed to parse errors, output the original content
|
||||
call go#util#EchoError(messages + [dir])
|
||||
call go#util#EchoError(self.messages + [self.dir])
|
||||
return
|
||||
endif
|
||||
|
||||
if winnr == winnr()
|
||||
call go#list#Populate(l:listtype, errors, join(args))
|
||||
if self.winnr == winnr()
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
if !bang
|
||||
if !self.bang
|
||||
call go#list#JumpToFirst(l:listtype)
|
||||
endif
|
||||
endif
|
||||
|
@ -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)
|
||||
|
@ -105,7 +105,6 @@ function! go#lint#Gometa(autosave, ...) abort
|
||||
|
||||
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:
|
||||
@ -147,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)
|
||||
@ -166,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)
|
||||
@ -175,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
|
||||
@ -228,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
|
||||
|
||||
@ -246,10 +244,18 @@ function! go#lint#ToggleMetaLinterAutoSave() abort
|
||||
endfunction
|
||||
|
||||
function! s:lint_job(args, autosave)
|
||||
let status_dir = expand('%:p:h')
|
||||
let started_at = reltime()
|
||||
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",
|
||||
@ -259,26 +265,18 @@ function! s:lint_job(args, autosave)
|
||||
call go#cmd#autowrite()
|
||||
|
||||
if a:autosave
|
||||
let l:listtype = go#list#Type("GoMetaLinterAutoSave")
|
||||
let state.listtype = go#list#Type("GoMetaLinterAutoSave")
|
||||
else
|
||||
let l:listtype = go#list#Type("GoMetaLinter")
|
||||
let state.listtype = go#list#Type("GoMetaLinter")
|
||||
endif
|
||||
|
||||
let l:errformat = '%f:%l:%c:%t%*[^:]:\ %m,%f:%l::%t%*[^:]:\ %m'
|
||||
|
||||
let l:messages = []
|
||||
let l:exited = 0
|
||||
let l:closed = 0
|
||||
let l:exit_status = 0
|
||||
let l:winnr = winnr()
|
||||
|
||||
function! s:callback(chan, msg) closure
|
||||
call add(messages, a:msg)
|
||||
function! s:callback(chan, msg) dict closure
|
||||
call add(self.messages, a:msg)
|
||||
endfunction
|
||||
|
||||
function! s:exit_cb(job, exitval) closure
|
||||
let exited = 1
|
||||
let exit_status = a:exitval
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
let self.exit_status = a:exitval
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
@ -290,50 +288,63 @@ function! s:lint_job(args, autosave)
|
||||
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)
|
||||
|
||||
if closed
|
||||
call s:show_errors()
|
||||
if self.closed
|
||||
call self.show_errors()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) closure
|
||||
let closed = 1
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if exited
|
||||
call s:show_errors()
|
||||
if self.exited
|
||||
call self.show_errors()
|
||||
endif
|
||||
endfunction
|
||||
|
||||
|
||||
function! s:show_errors() closure
|
||||
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 l:listtype == 'locationlist'
|
||||
exe l:winnr . "wincmd w"
|
||||
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(l:listtype, l:errorformat, messages, 'GoMetaLinter')
|
||||
call go#list#ParseFormat(self.listtype, l:errorformat, self.messages, 'GoMetaLinter')
|
||||
|
||||
let errors = go#list#Get(l:listtype)
|
||||
call go#list#Window(l:listtype, len(errors))
|
||||
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)
|
||||
call go#util#EchoSuccess("linting finished")
|
||||
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"),
|
||||
\ 'close_cb': funcref("s:close_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)
|
||||
|
@ -3,7 +3,7 @@ func! Test_Gometa() abort
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': 3, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
||||
\ {'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
|
||||
@ -37,7 +37,7 @@ func! Test_GometaWithDisabled() abort
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': 3, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingFooDoc should have comment or be unexported (golint)'}
|
||||
\ {'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
|
||||
@ -71,7 +71,7 @@ func! Test_GometaAutoSave() abort
|
||||
silent exe 'e ' . $GOPATH . '/src/lint/lint.go'
|
||||
|
||||
let expected = [
|
||||
\ {'lnum': 5, 'bufnr': 2, 'col': 1, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': 'w', 'pattern': '', 'text': 'exported function MissingDoc should have comment or be unexported (golint)'}
|
||||
\ {'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()
|
||||
@ -102,4 +102,30 @@ func! Test_GometaAutoSave() abort
|
||||
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
|
||||
|
@ -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
|
||||
|
||||
|
@ -72,20 +72,22 @@ function! go#rename#Rename(bang, ...) abort
|
||||
endfunction
|
||||
|
||||
function s:rename_job(args)
|
||||
let exited = 0
|
||||
let closed = 0
|
||||
let exitval = 0
|
||||
let messages = []
|
||||
let state = {
|
||||
\ 'exited': 0,
|
||||
\ 'closed': 0,
|
||||
\ 'exitval': 0,
|
||||
\ 'messages': [],
|
||||
\ 'status_dir': expand('%:p:h'),
|
||||
\ 'bang': a:args.bang
|
||||
\ }
|
||||
|
||||
function! s:callback(chan, msg) closure
|
||||
call add(messages, a:msg)
|
||||
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) closure
|
||||
let exited = 1
|
||||
let exitval = a:exitval
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
let self.exitval = a:exitval
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
@ -97,28 +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)
|
||||
|
||||
if closed
|
||||
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) closure
|
||||
let closed = 1
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if exited
|
||||
call s:parse_errors(exitval, a:args.bang, messages)
|
||||
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"),
|
||||
\ 'close_cb': funcref("s:close_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",
|
||||
@ -156,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
sources_non_forked/vim-go/autoload/go/term_test.vim
Normal file
50
sources_non_forked/vim-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
|
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
notafunc()
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
str := "hello world!"
|
||||
fmt.Printf("%d\n", str)
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello, world")
|
||||
}
|
@ -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")
|
||||
@ -139,9 +138,6 @@ function! go#test#Func(bang, ...) abort
|
||||
endfunction
|
||||
|
||||
function! s:test_job(args) abort
|
||||
let status_dir = expand('%:p:h')
|
||||
let started_at = reltime()
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'current status',
|
||||
\ 'type': "test",
|
||||
@ -152,23 +148,29 @@ 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 l:exited = 0
|
||||
let l:closed = 0
|
||||
let l:exitval = 0
|
||||
let messages = []
|
||||
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()
|
||||
\ }
|
||||
|
||||
function! s:callback(chan, msg) closure
|
||||
call add(messages, a:msg)
|
||||
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
|
||||
let exited = 1
|
||||
let exitval = a:exitval
|
||||
function! s:exit_cb(job, exitval) dict
|
||||
let self.exited = 1
|
||||
let self.exitval = a:exitval
|
||||
|
||||
let status = {
|
||||
\ 'desc': 'last status',
|
||||
@ -176,7 +178,7 @@ function! s:test_job(args) abort
|
||||
\ 'state': "pass",
|
||||
\ }
|
||||
|
||||
if a:args.compile_test
|
||||
if self.compile_test
|
||||
let status.state = "success"
|
||||
endif
|
||||
|
||||
@ -186,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")
|
||||
@ -196,31 +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)
|
||||
|
||||
if closed
|
||||
call s:show_errors(a:args, l:exitval, messages)
|
||||
if self.closed
|
||||
call s:show_errors(self.args, self.exitval, self.messages)
|
||||
endif
|
||||
endfunction
|
||||
|
||||
function! s:close_cb(ch) closure
|
||||
let closed = 1
|
||||
function! s:close_cb(ch) dict
|
||||
let self.closed = 1
|
||||
|
||||
if exited
|
||||
call s:show_errors(a:args, l:exitval, messages)
|
||||
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"),
|
||||
\ 'close_cb': funcref("s:close_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()
|
||||
@ -241,7 +245,6 @@ 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)
|
||||
call go#list#Window(l:listtype)
|
||||
return
|
||||
endif
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user