mirror of
				https://github.com/amix/vimrc
				synced 2025-10-31 06:33:35 +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
	 Amir Salihefendic
					Amir Salihefendic