1
0
mirror of https://github.com/amix/vimrc synced 2025-06-16 01:25:00 +08:00

Updated plugins

This commit is contained in:
Amir Salihefendic
2018-09-24 21:40:17 -03:00
parent 6bd9eda8c3
commit a6b64938eb
199 changed files with 2897 additions and 980 deletions

View File

@ -31,15 +31,12 @@ function! go#cmd#Build(bang, ...) abort
\ [".", "errors"]
" Vim and Neovim async.
if go#util#has_job() || has('nvim')
if go#config#EchoCommandInfo()
call go#util#EchoProgress("building dispatched ...")
endif
if go#util#has_job()
call s:cmd_job({
\ 'cmd': ['go'] + args,
\ 'bang': a:bang,
\ 'for': 'GoBuild',
\ 'statustype': 'build'
\})
" Vim 7.4 without async
@ -195,14 +192,11 @@ function! go#cmd#Install(bang, ...) abort
" expand all wildcards(i.e: '%' to the current file name)
let goargs = map(copy(a:000), "expand(v:val)")
if go#config#EchoCommandInfo()
call go#util#EchoProgress("installing dispatched ...")
endif
call s:cmd_job({
\ 'cmd': ['go', 'install', '-tags', go#config#BuildTags()] + goargs,
\ 'bang': a:bang,
\ 'for': 'GoInstall',
\ 'statustype': 'install'
\})
return
endif
@ -281,16 +275,7 @@ endfunction
" | Vim job callbacks |
" ---------------------
function s:cmd_job(args) abort
let status_dir = expand('%:p:h')
let started_at = reltime()
call go#statusline#Update(status_dir, {
\ 'desc': "current status",
\ 'type': a:args.cmd[1],
\ 'state': "started",
\})
function! s:cmd_job(args) abort
" autowrite is not enabled for jobs
call go#cmd#autowrite()

View File

@ -9,6 +9,15 @@ function! s:gocodeCommand(cmd, args) abort
let cmd = [bin_path]
let cmd = extend(cmd, ['-sock', socket_type])
let cmd = extend(cmd, ['-f', 'vim'])
if go#config#GocodeProposeBuiltins()
let cmd = extend(cmd, ['-builtin'])
endif
if go#config#GocodeProposeSource()
let cmd = extend(cmd, ['-source'])
endif
let cmd = extend(cmd, [a:cmd])
let cmd = extend(cmd, a:args)
@ -43,31 +52,7 @@ function! s:sync_gocode(cmd, args, input) abort
return l:result
endfunction
let s:optionsEnabled = 0
function! s:gocodeEnableOptions() abort
if s:optionsEnabled
return
endif
let l:bin_path = go#path#CheckBinPath("gocode")
if empty(l:bin_path)
return
endif
let s:optionsEnabled = 1
call go#util#Exec(['gocode', 'set', 'propose-builtins', s:toBool(go#config#GocodeProposeBuiltins())])
call go#util#Exec(['gocode', 'set', 'autobuild', s:toBool(go#config#GocodeAutobuild())])
call go#util#Exec(['gocode', 'set', 'unimported-packages', s:toBool(go#config#GocodeUnimportedPackages())])
endfunction
function! s:toBool(val) abort
if a:val | return 'true' | else | return 'false' | endif
endfunction
function! s:gocodeAutocomplete() abort
call s:gocodeEnableOptions()
" use the offset as is, because the cursor position is the position for
" which autocomplete candidates are needed.
return s:sync_gocode('autocomplete',
@ -81,60 +66,36 @@ function! go#complete#GetInfo() abort
return s:sync_info(0)
endfunction
function! go#complete#Info() abort
function! go#complete#Info(showstatus) abort
if go#util#has_job(1)
return s:async_info(1)
return s:async_info(1, a:showstatus)
else
return s:sync_info(1)
endif
endfunction
function! s:async_info(echo)
if exists("s:async_info_job")
call job_stop(s:async_info_job)
unlet s:async_info_job
endif
function! s:async_info(echo, showstatus)
let state = {'echo': a:echo}
let state = {
\ 'exited': 0,
\ 'exit_status': 0,
\ 'closed': 0,
\ 'messages': [],
\ 'echo': a:echo
\ }
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
function! s:complete(job, exit_status, messages) abort dict
if a:exit_status != 0
return
endif
let result = s:info_filter(self.echo, join(self.messages, "\n"))
if &encoding != 'utf-8'
let i = 0
while i < len(a:messages)
let a:messages[i] = iconv(a:messages[i], 'utf-8', &encoding)
let i += 1
endwhile
endif
let result = s:info_filter(self.echo, join(a:messages, "\n"))
call s:info_complete(self.echo, result)
endfunction
" explicitly bind complete to state so that within it, self will
" always refer to state. See :help Partial for more information.
let state.complete = function('s:complete', [], state)
" add 1 to the offset, so that the position at the cursor will be included
" in gocode's search
@ -146,23 +107,32 @@ function! s:async_info(echo)
\ "GOROOT": go#util#env("goroot")
\ }
let opts = {
\ 'bang': 1,
\ 'complete': state.complete,
\ 'for': '_',
\ }
if a:showstatus
let opts.statustype = 'gocode'
endif
let opts = go#job#Options(l:opts)
let cmd = s:gocodeCommand('autocomplete',
\ [expand('%:p'), offset])
" TODO(bc): Don't write the buffer to a file; pass the buffer directrly to
" TODO(bc): Don't write the buffer to a file; pass the buffer directly 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 = {
" doesn't work.
call extend(opts, {
\ '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)
call go#job#Start(cmd, opts)
endfunction
function! s:gocodeFile()

View File

@ -0,0 +1,20 @@
func! Test_GetInfo()
let l:filename = 'complete/complete.go'
let l:tmp = gotest#load_fixture(l:filename)
call cursor(8, 3)
let g:go_info_mode = 'gocode'
let expected = 'func Example(s string)'
let actual = go#complete#GetInfo()
call assert_equal(expected, actual)
let g:go_info_mode = 'guru'
call go#config#InfoMode()
let actual = go#complete#GetInfo()
call assert_equal(expected, actual)
unlet g:go_info_mode
endfunction
" vim: sw=2 ts=2 et

View File

@ -135,10 +135,6 @@ function! go#config#SetGuruScope(scope) abort
endif
endfunction
function! go#config#GocodeUnimportedPackages() abort
return get(g:, 'go_gocode_unimported_packages', 0)
endfunction
let s:sock_type = (has('win32') || has('win64')) ? 'tcp' : 'unix'
function! go#config#GocodeSocketType() abort
return get(g:, 'go_gocode_socket_type', s:sock_type)
@ -148,8 +144,8 @@ function! go#config#GocodeProposeBuiltins() abort
return get(g:, 'go_gocode_propose_builtins', 1)
endfunction
function! go#config#GocodeAutobuild() abort
return get(g:, 'go_gocode_autobuild', 1)
function! go#config#GocodeProposeSource() abort
return get(g:, 'go_gocode_propose_source', 1)
endfunction
function! go#config#EchoCommandInfo() abort
@ -282,6 +278,14 @@ function! go#config#SetAsmfmtAutosave(value) abort
let g:go_asmfmt_autosave = a:value
endfunction
function! go#config#ModFmtAutosave() abort
return get(g:, "go_mod_fmt_autosave", 1)
endfunction
function! go#config#SetModFmtAutosave(value) abort
let g:go_mod_fmt_autosave = a:value
endfunction
function! go#config#DocMaxHeight() abort
return get(g:, "go_doc_max_height", 20)
endfunction
@ -416,7 +420,7 @@ function! go#config#HighlightVariableDeclarations() abort
return get(g:, 'go_highlight_variable_declarations', 0)
endfunction
function go#config#FoldEnable(...) abort
function! go#config#FoldEnable(...) abort
if a:0 > 0
return index(go#config#FoldEnable(), a:1) > -1
endif

View File

@ -44,11 +44,7 @@ function! go#coverage#Buffer(bang, ...) abort
let s:toggle = 1
let l:tmpname = tempname()
if go#config#EchoCommandInfo()
call go#util#EchoProgress("testing...")
endif
if go#util#has_job() || has('nvim')
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname] + a:000,
\ 'complete': function('s:coverage_callback', [l:tmpname]),
@ -59,6 +55,10 @@ function! go#coverage#Buffer(bang, ...) abort
return
endif
if go#config#EchoCommandInfo()
call go#util#EchoProgress("testing...")
endif
let args = [a:bang, 0, "-coverprofile", l:tmpname]
if a:0
call extend(args, a:000)
@ -89,12 +89,13 @@ endfunction
" a new HTML coverage page from that profile in a new browser
function! go#coverage#Browser(bang, ...) abort
let l:tmpname = tempname()
if go#util#has_job() || has('nvim')
if go#util#has_job()
call s:coverage_job({
\ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname],
\ 'complete': function('s:coverage_browser_callback', [l:tmpname]),
\ 'bang': a:bang,
\ 'for': 'GoTest',
\ 'statustype': 'coverage',
\ })
return
endif

View File

@ -25,12 +25,12 @@ function! s:groutineID() abort
return s:state['currentThread'].goroutineID
endfunction
function! s:exit(job, status) abort
function! s:complete(job, exit_status, data) abort
if has_key(s:state, 'job')
call remove(s:state, 'job')
endif
call s:clearState()
if a:status > 0
if a:exit_status > 0
call go#util#EchoError(s:state['message'])
endif
endfunction
@ -567,14 +567,18 @@ function! go#debug#Start(is_test, ...) abort
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',
\})
let l:opts = {
\ 'for': '_',
\ 'statustype': 'debug',
\ 'complete': function('s:complete'),
\ }
let l:opts = go#job#Options(l:opts)
let l:opts.out_cb = function('s:out_cb')
let l:opts.err_cb = function('s:err_cb')
let l:opts.stoponexit = 'kill'
let s:state['job'] = go#job#Start(l:cmd, l:opts)
catch
call go#util#EchoError(v:exception)
endtry

View File

@ -42,20 +42,20 @@ function! go#def#Jump(mode) abort
call extend(cmd, ["definition", fname . ':#' . go#util#OffsetCursor()])
if go#util#has_job() || has('nvim')
if go#util#has_job()
let l:state = {}
let l:spawn_args = {
\ 'cmd': cmd,
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name]),
\ 'complete': function('s:jump_to_declaration_cb', [a:mode, bin_name], l:state),
\ 'for': '_',
\ 'statustype': 'searching declaration',
\ }
if &modified
let l:spawn_args.input = stdin_content
endif
call go#util#EchoProgress("searching declaration ...")
call s:def_job(spawn_args)
call s:def_job(spawn_args, l:state)
return
endif
@ -77,13 +77,17 @@ function! go#def#Jump(mode) abort
call go#def#jump_to_declaration(out, a:mode, bin_name)
endfunction
function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort
function! s:jump_to_declaration_cb(mode, bin_name, job, exit_status, data) abort dict
if a:exit_status != 0
return
endif
call go#def#jump_to_declaration(a:data[0], a:mode, a:bin_name)
call go#util#EchoSuccess(fnamemodify(a:data[0], ":t"))
" capture the active window so that after the exit_cb and close_cb callbacks
" can return to it when a:mode caused a split.
let self.winid = win_getid(winnr())
endfunction
function! go#def#jump_to_declaration(out, mode, bin_name) abort
@ -283,9 +287,26 @@ function! go#def#Stack(...) abort
endif
endfunction
function s:def_job(args) abort
function s:def_job(args, state) abort
let l:start_options = go#job#Options(a:args)
let l:state = a:state
function! s:exit_cb(next, job, exitval) dict
call call(a:next, [a:job, a:exitval])
if has_key(self, 'winid')
call win_gotoid(self.winid)
endif
endfunction
let l:start_options.exit_cb = funcref('s:exit_cb', [l:start_options.exit_cb], l:state)
function! s:close_cb(next, ch) dict
call call(a:next, [a:ch])
if has_key(self, 'winid')
call win_gotoid(self.winid)
endif
endfunction
let l:start_options.close_cb = funcref('s:close_cb', [l:start_options.close_cb], l:state)
if &modified
let l:tmpname = tempname()
call writefile(split(a:args.input, "\n"), l:tmpname, "b")
@ -293,7 +314,7 @@ function s:def_job(args) abort
let l:start_options.in_name = l:tmpname
endif
call go#job#Start(a:args.cmd, start_options)
call go#job#Start(a:args.cmd, l:start_options)
endfunction
" vim: sw=2 ts=2 et

View File

@ -45,7 +45,7 @@ func! Test_Jump_leaves_lists() abort
call setqflist(copy(expected), 'r' )
let l:bufnr = bufnr('%')
call cursor(6, 3)
call cursor(6, 7)
call go#def#Jump('')
let start = reltime()

View File

@ -106,64 +106,6 @@ function! s:sync_guru(args) abort
return l: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}
let stdout_buf = ""
function opts.on_stdout(job_id, data, event) closure
let l:data = a:data
let l:data[0] = stdout_buf . l:data[0]
let stdout_buf = ""
if l:data[-1] != ""
let stdout_buf = l:data[-1]
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call a:start_options.callback(a:job_id, join(l:data, "\n"))
endfunction
let stderr_buf = ""
function opts.on_stderr(job_id, data, event) closure
let l:data = a:data
let l:data[0] = stderr_buf . l:data[0]
let stderr_buf = ""
if l:data[-1] != ""
let stderr_buf = l:data[-1]
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call a:start_options.callback(a:job_id, join(l: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', go#util#Shelljoin(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)
@ -172,91 +114,50 @@ function! s:async_guru(args) abort
return
endif
if !has_key(a:args, 'disable_progress')
if a:args.needs_scope
call go#util#EchoProgress("analysing with scope " . result.scope .
\ " (see ':help go-guru-scope' if this doesn't work)...")
endif
endif
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)
function! s:complete(job, exit_status, messages) dict abort
let output = join(a:messages, "\n")
call self.parse(a:exit_status, output, self.mode)
endfunction
" explicitly bind complete to state so that within it, self will
" always refer to state. See :help Partial for more information.
let state.complete = function('s:complete', [], state)
function! s:exit_cb(job, exitval) dict
let self.exited = 1
let opts = {
\ 'statustype': get(a:args, 'statustype', a:args.mode),
\ 'for': '_',
\ 'errorformat': "%f:%l.%c-%[%^:]%#:\ %m,%f:%l:%c:\ %m",
\ 'complete': state.complete,
\ }
let status = {
\ 'desc': 'last status',
\ 'type': self.statusline_type,
\ 'state': "finished",
\ }
if has_key(a:args, 'disable_progress')
let opts.statustype = ''
endif
if a:exitval
let self.exitval = a:exitval
let status.state = "failed"
endif
call go#statusline#Update(self.status_dir, status)
if self.closed
call self.complete()
endif
endfunction
function! s:close_cb(ch) dict
let self.closed = 1
if self.exited
call self.complete()
endif
endfunction
function state.complete() dict
let out = join(self.messages, "\n")
call self.parse(self.exitval, out, self.mode)
endfunction
" explicitly bind the callbacks to state so that self within them always
" refers to state. See :help Partial for more information.
let start_options = {
\ 'callback': function('s:callback', [], state),
\ 'exit_cb': function('s:exit_cb', [], state),
\ 'close_cb': function('s:close_cb', [], state)
\ }
let opts = go#job#Options(l:opts)
if has_key(result, 'stdin_content')
let l:tmpname = tempname()
call writefile(split(result.stdin_content, "\n"), l:tmpname, "b")
let l:start_options.in_io = "file"
let l:start_options.in_name = l:tmpname
let l:opts.in_io = "file"
let l:opts.in_name = l:tmpname
endif
call go#statusline#Update(state.status_dir, {
\ 'desc': "current status",
\ 'type': state.statusline_type,
\ 'state': "analysing",
\})
call go#job#Start(result.cmd, opts)
return s:job_start(result.cmd, start_options)
if a:args.needs_scope && go#config#EchoCommandInfo() && !has_key(a:args, 'disable_progress')
call go#util#EchoProgress("analysing with scope " . result.scope .
\ " (see ':help go-guru-scope' if this doesn't work)...")
endif
endfunc
" run_guru runs the given guru argument
function! s:run_guru(args) abort
if has('nvim') || go#util#has_job()
if go#util#has_job()
let res = s:async_guru(a:args)
else
let res = s:sync_guru(a:args)
@ -320,7 +221,7 @@ function! go#guru#Describe(selected) abort
call s:run_guru(args)
endfunction
function! go#guru#DescribeInfo() abort
function! go#guru#DescribeInfo(showstatus) abort
" json_encode() and friends are introduced with this patch (7.4.1304)
" vim: https://groups.google.com/d/msg/vim_dev/vLupTNhQhZ8/cDGIk0JEDgAJ
" nvim: https://github.com/neovim/neovim/pull/4131
@ -411,7 +312,7 @@ function! go#guru#DescribeInfo() abort
return
endif
call go#util#EchoInfo(info)
echo "vim-go: " | echohl Function | echon info | echohl None
endfunction
let args = {
@ -504,7 +405,7 @@ function! go#guru#Referrers(selected) abort
call s:run_guru(args)
endfunction
function! go#guru#SameIds() abort
function! go#guru#SameIds(showstatus) 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
if !exists("*matchaddpos")
@ -527,6 +428,9 @@ function! go#guru#SameIds() abort
\ 'needs_scope': 0,
\ 'custom_parse': function('s:same_ids_highlight'),
\ }
if !a:showstatus
let args.disable_progress = 1
endif
call s:run_guru(args)
endfunction

View File

@ -65,6 +65,9 @@ function! go#import#SwitchImport(enabled, localname, path, bang) abort
let packageline = line
let appendline = line
elseif linestr =~# '^import\s\+(\+)'
let appendline = line
let appendstr = qlocalpath
elseif linestr =~# '^import\s\+('
let appendstr = qlocalpath
let indentstr = 1
@ -161,8 +164,16 @@ function! go#import#SwitchImport(enabled, localname, path, bang) abort
let linesdelta += 3
let appendstr = qlocalpath
let indentstr = 1
call append(appendline, appendstr)
elseif getline(appendline) =~# '^import\s\+(\+)'
call setline(appendline, 'import (')
call append(appendline + 0, appendstr)
call append(appendline + 1, ')')
let linesdelta -= 1
let indentstr = 1
else
call append(appendline, appendstr)
endif
call append(appendline, appendstr)
execute appendline + 1
if indentstr
execute 'normal! >>'

View File

@ -0,0 +1,22 @@
func! Test_indent_raw_string() abort
try
let l:dir= gotest#write_file('indent/indent.go', [
\ 'package main',
\ '',
\ 'import "fmt"',
\ '',
\ 'func main() {',
\ "\t\x1fconst msg = `",
\ '`',
\ '\tfmt.Println(msg)',
\ '}'])
silent execute "normal o" . "not indented\<Esc>"
let l:indent = indent(line('.'))
call assert_equal(0, l:indent)
finally
call delete(l:dir, 'rf')
endtry
endfunc
" vim: sw=2 ts=2 et

View File

@ -33,7 +33,10 @@ endfunction
" function will be passed three arguments: the job, its exit code, and the
" list of messages received from the channel. The default is a no-op. A
" custom value can modify the messages before they are processed by the
" returned exit_cb and close_cb callbacks.
" returned exit_cb and close_cb callbacks. When the function is called,
" the current window will be the window that was hosting the buffer when
" the job was started. After it returns, the current window will be
" restored to what it was before the function was called.
" The return value is a dictionary with these keys:
" 'callback':
@ -87,14 +90,27 @@ function! go#job#Options(args)
" do nothing in state.complete by default.
function state.complete(job, exit_status, data)
if has_key(self, 'custom_complete')
let l:winid = win_getid(winnr())
" Always set the active window to the window that was active when the job
" was started. Among other things, this makes sure that the correct
" window's location list will be populated when the list type is
" 'location' and the user has moved windows since starting the job.
call win_gotoid(self.winid)
call self.custom_complete(a:job, a:exit_status, a:data)
call win_gotoid(l:winid)
endif
call self.show_errors(a:job, a:exit_status, a:data)
endfunction
function state.show_status(job, exit_status) dict
if self.statustype == ''
return
endif
if go#config#EchoCommandInfo()
let prefix = ""
if self.statustype != ''
let prefix = '[' . self.statustype . '] '
endif
let prefix = '[' . self.statustype . '] '
if a:exit_status == 0
call go#util#EchoSuccess(prefix . "SUCCESS")
else
@ -102,10 +118,6 @@ function! go#job#Options(args)
endif
endif
if self.statustype == ''
return
endif
let status = {
\ 'desc': 'last status',
\ 'type': self.statustype,
@ -127,10 +139,15 @@ function! go#job#Options(args)
endfunction
if has_key(a:args, 'complete')
let state.complete = a:args.complete
let state.custom_complete = a:args.complete
endif
function! s:start(args) dict
if go#config#EchoCommandInfo() && self.statustype != ""
let prefix = '[' . self.statustype . '] '
call go#util#EchoSuccess(prefix . "dispatched")
endif
if self.statustype != ''
let status = {
\ 'desc': 'current status',
@ -164,7 +181,6 @@ function! go#job#Options(args)
if self.closed || has('nvim')
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
@ -177,7 +193,6 @@ function! go#job#Options(args)
if self.exited
let job = ch_getjob(a:ch)
call self.complete(job, self.exit_status, self.messages)
call self.show_errors(job, self.exit_status, self.messages)
endif
endfunction
" explicitly bind close_cb to state so that within it, self will
@ -225,7 +240,7 @@ function! go#job#Options(args)
if empty(errors)
" failed to parse errors, output the original content
call go#util#EchoError(self.messages + [self.dir])
call go#util#EchoError([self.dir] + self.messages)
call win_gotoid(l:winid)
return
endif
@ -254,10 +269,21 @@ function! go#job#Start(cmd, options)
let l:options = s:neooptions(l:options)
endif
" Verify that the working directory for the job actually exists. Return
" early if the directory does not exist. This helps avoid errors when
" working with plugins that use virtual files that don't actually exist on
" the file system.
let filedir = expand("%:p:h")
if has_key(l:options, 'cwd') && !isdirectory(l:options.cwd)
return
elseif !isdirectory(filedir)
return
endif
if !has_key(l:options, 'cwd')
" pre start
let dir = getcwd()
execute l:cd fnameescape(expand("%:p:h"))
execute l:cd fnameescape(filedir)
endif
if has_key(l:options, '_start')
@ -267,11 +293,10 @@ function! go#job#Start(cmd, options)
unlet l:options._start
endif
if has('nvim')
let l:input = []
if has_key(l:options, 'in_io') && l:options.in_io ==# 'file' && !empty(l:options.in_name)
let l:input = readfile(l:options.in_name, 1)
if has_key(a:options, 'in_io') && a:options.in_io ==# 'file' && !empty(a:options.in_name)
let l:input = readfile(a:options.in_name, "b")
endif
let job = jobstart(a:cmd, l:options)
@ -306,49 +331,77 @@ function! s:neooptions(options)
continue
endif
" dealing with the channel lines of Neovim sucks. The docs (:help
" channel-lines) say:
" stream event handlers may receive partial (incomplete) lines. For a
" given invocation of on_stdout etc, `a:data` is not guaranteed to end
" with a newline.
" - `abcdefg` may arrive as `['abc']`, `['defg']`.
" - `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`,
" `['','efg']`, or even `['ab']`, `['c','efg']`.
if key == 'callback'
let l:options['callback'] = a:options['callback']
if !has_key(a:options, 'out_cb')
let l:options['stdout_buffered'] = v:true
function! s:callback2on_stdout(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stdout_buf . l:data[0]
let self.stdout_buf = ""
" a single empty string means EOF was reached.
if len(a:data) == 1 && a:data[0] == ''
" when there's nothing buffered, return early so that an
" erroneous message will not be added.
if self.stdout_buf == ''
return
endif
if l:data[-1] != ""
let l:data = [self.stdout_buf]
let self.stdout_buf = ''
else
let l:data = copy(a:data)
let l:data[0] = self.stdout_buf . l:data[0]
" The last element may be a partial line; save it for next time.
let self.stdout_buf = l:data[-1]
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.callback(a:ch, join(l:data, "\n"))
for l:msg in l:data
call self.callback(a:ch, l:msg)
endfor
endfunction
let l:options['on_stdout'] = function('s:callback2on_stdout', [], l:options)
endif
if !has_key(a:options, 'err_cb')
let l:options['stderr_buffered'] = v:true
function! s:callback2on_stderr(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stderr_buf . l:data[0]
let self.stderr_buf = ""
" a single empty string means EOF was reached.
if len(a:data) == 1 && a:data[0] == ''
" when there's nothing buffered, return early so that an
" erroneous message will not be added.
if self.stderr_buf == ''
return
endif
let l:data = [self.stderr_buf]
let self.stderr_buf = ''
else
let l:data = copy(a:data)
let l:data[0] = self.stderr_buf . l:data[0]
if l:data[-1] != ""
" The last element may be a partial line; save it for next time.
let self.stderr_buf = l:data[-1]
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.callback(a:ch, join(l:data, "\n"))
for l:msg in l:data
call self.callback(a:ch, l:msg)
endfor
endfunction
let l:options['on_stderr'] = function('s:callback2on_stderr', [], l:options)
endif
@ -358,22 +411,32 @@ function! s:neooptions(options)
if key == 'out_cb'
let l:options['out_cb'] = a:options['out_cb']
let l:options['stdout_buffered'] = v:true
function! s:on_stdout(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stdout_buf . l:data[0]
let self.stdout_buf = ""
" a single empty string means EOF was reached.
if len(a:data) == 1 && a:data[0] == ''
" when there's nothing buffered, return early so that an
" erroneous message will not be added.
if self.stdout_buf == ''
return
endif
let l:data = [self.stdout_buf]
let self.stdout_buf = ''
else
let l:data = copy(a:data)
let l:data[0] = self.stdout_buf . l:data[0]
if l:data[-1] != ""
" The last element may be a partial line; save it for next time.
let self.stdout_buf = l:data[-1]
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.out_cb(a:ch, join(l:data, "\n"))
for l:msg in l:data
call self.out_cb(a:ch, l:msg)
endfor
endfunction
let l:options['on_stdout'] = function('s:on_stdout', [], l:options)
@ -382,22 +445,32 @@ function! s:neooptions(options)
if key == 'err_cb'
let l:options['err_cb'] = a:options['err_cb']
let l:options['stderr_buffered'] = v:true
function! s:on_stderr(ch, data, event) dict
let l:data = a:data
let l:data[0] = self.stderr_buf . l:data[0]
let self.stderr_buf = ""
" a single empty string means EOF was reached.
if len(a:data) == 1 && a:data[0] == ''
" when there's nothing buffered, return early so that an
" erroneous message will not be added.
if self.stderr_buf == ''
return
endif
let l:data = [self.stderr_buf]
let self.stderr_buf = ''
else
let l:data = copy(a:data)
let l:data[0] = self.stderr_buf . l:data[0]
if l:data[-1] != ""
" The last element may be a partial line; save it for next time.
let self.stderr_buf = l:data[-1]
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
endif
let l:data = l:data[:-2]
if len(l:data) == 0
return
endif
call self.err_cb(a:ch, join(l:data, "\n"))
for l:msg in l:data
call self.err_cb(a:ch, l:msg)
endfor
endfunction
let l:options['on_stderr'] = function('s:on_stderr', [], l:options)
@ -418,6 +491,12 @@ function! s:neooptions(options)
continue
endif
if key == 'stoponexit'
if a:options['stoponexit'] == ''
let l:options['detach'] = 1
endif
continue
endif
endfor
return l:options
endfunction

View File

@ -42,7 +42,7 @@ function! go#lint#Gometa(autosave, ...) abort
" Include only messages for the active buffer for autosave.
let include = [printf('--include=^%s:.*$', fnamemodify(expand('%:p'), ":."))]
if go#util#has_job() || has('nvim')
if go#util#has_job()
let include = [printf('--include=^%s:.*$', expand('%:p:t'))]
endif
let cmd += include
@ -56,7 +56,7 @@ function! go#lint#Gometa(autosave, ...) abort
let cmd += goargs
if go#util#has_job() || has('nvim')
if go#util#has_job()
call s:lint_job({'cmd': cmd}, a:autosave)
return
endif
@ -207,36 +207,10 @@ function! s:lint_job(args, autosave)
let l:opts.for = "GoMetaLinterAutoSave"
endif
let l:cbs = go#job#Options(l:opts)
if a:autosave
" 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.
function! s:exit_cb(next, job, exitval)
let l:winid = win_getid(winnr())
call call(a:next, [a:job, a:exitval])
call win_gotoid(l:winid)
endfunction
" wrap l:cbs.exit_cb in s:exit_cb.
let l:cbs.exit_cb = funcref('s:exit_cb', [l:cbs.exit_cb])
function! s:close_cb(next, ch)
let l:winid = win_getid(winnr())
call call(a:next, [a:ch])
call win_gotoid(l:winid)
endfunction
" wrap l:cbs.close_cb in s:close_cb.
let l:cbs.close_cb = funcref('s:close_cb', [l:cbs.close_cb])
endif
" autowrite is not enabled for jobs
call go#cmd#autowrite()
call go#job#Start(a:args.cmd, l:cbs)
call go#job#Spawn(a:args.cmd, l:opts)
endfunction
" vim: sw=2 ts=2 et

View File

@ -141,6 +141,7 @@ let s:default_list_type_commands = {
\ "GoLint": "quickfix",
\ "GoMetaLinter": "quickfix",
\ "GoMetaLinterAutoSave": "locationlist",
\ "GoModFmt": "locationlist",
\ "GoModifyTags": "locationlist",
\ "GoRename": "quickfix",
\ "GoRun": "quickfix",

View File

@ -0,0 +1,140 @@
let s:go_major_version = ""
function! go#mod#Format() abort
" go mod only exists in `v1.11`
if empty(s:go_major_version)
let tokens = matchlist(go#util#System("go version"), '\d\+.\(\d\+\) ')
let s:go_major_version = str2nr(tokens[1])
endif
if s:go_major_version < "11"
call go#util#EchoError("Go v1.11 is required to format go.mod file")
return
endif
let fname = fnamemodify(expand("%"), ':p:gs?\\?/?')
" Save cursor position and many other things.
let l:curw = winsaveview()
" Write current unsaved buffer to a temp file
let l:tmpname = tempname() . '.mod'
call writefile(go#util#GetLines(), l:tmpname)
if go#util#IsWin()
let l:tmpname = tr(l:tmpname, '\', '/')
endif
let current_col = col('.')
let l:args = ['go', 'mod', 'edit', '--fmt', l:tmpname]
let [l:out, l:err] = go#util#Exec(l:args)
let diff_offset = len(readfile(l:tmpname)) - line('$')
if l:err == 0
call go#mod#update_file(l:tmpname, fname)
else
let errors = s:parse_errors(fname, l:out)
call s:show_errors(errors)
endif
" We didn't use the temp file, so clean up
call delete(l:tmpname)
" Restore our cursor/windows positions.
call winrestview(l:curw)
" be smart and jump to the line the new statement was added/removed
call cursor(line('.') + diff_offset, current_col)
" Syntax highlighting breaks less often.
syntax sync fromstart
endfunction
" update_file updates the target file with the given formatted source
function! go#mod#update_file(source, target)
" remove undo point caused via BufWritePre
try | silent undojoin | catch | endtry
let old_fileformat = &fileformat
if exists("*getfperm")
" save file permissions
let original_fperm = getfperm(a:target)
endif
call rename(a:source, a:target)
" restore file permissions
if exists("*setfperm") && original_fperm != ''
call setfperm(a:target , original_fperm)
endif
" reload buffer to reflect latest changes
silent edit!
let &fileformat = old_fileformat
let &syntax = &syntax
let l:listtype = go#list#Type("GoModFmt")
" the title information was introduced with 7.4-2200
" https://github.com/vim/vim/commit/d823fa910cca43fec3c31c030ee908a14c272640
if has('patch-7.4.2200')
" clean up previous list
if l:listtype == "quickfix"
let l:list_title = getqflist({'title': 1})
else
let l:list_title = getloclist(0, {'title': 1})
endif
else
" can't check the title, so assume that the list was for go fmt.
let l:list_title = {'title': 'Format'}
endif
if has_key(l:list_title, "title") && l:list_title['title'] == "Format"
call go#list#Clean(l:listtype)
endif
endfunction
" parse_errors parses the given errors and returns a list of parsed errors
function! s:parse_errors(filename, content) abort
let splitted = split(a:content, '\n')
" list of errors to be put into location list
let errors = []
for line in splitted
let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\s*\(.*\)')
if !empty(tokens)
call add(errors,{
\"filename": a:filename,
\"lnum": tokens[2],
\"text": tokens[3],
\ })
endif
endfor
return errors
endfunction
" show_errors opens a location list and shows the given errors. If the given
" errors is empty, it closes the the location list
function! s:show_errors(errors) abort
let l:listtype = go#list#Type("GoModFmt")
if !empty(a:errors)
call go#list#Populate(l:listtype, a:errors, 'Format')
call go#util#EchoError("GoModFmt returned error")
endif
" this closes the window if there are no errors or it opens
" it if there is any
call go#list#Window(l:listtype, len(a:errors))
endfunction
function! go#mod#ToggleModFmtAutoSave() abort
if go#config#ModFmtAutosave()
call go#config#SetModFmtAutosave(0)
call go#util#EchoProgress("auto mod fmt disabled")
return
end
call go#config#SetModFmtAutosave(1)
call go#util#EchoProgress("auto mod fmt enabled")
endfunction

View File

@ -27,8 +27,7 @@ function! go#rename#Rename(bang, ...) abort
let offset = printf('%s:#%d', fname, pos)
let cmd = [bin_path, "-offset", offset, "-to", to_identifier, '-tags', go#config#BuildTags()]
if go#util#has_job() || has('nvim')
call go#util#EchoProgress(printf("renaming to '%s' ...", to_identifier))
if go#util#has_job()
call s:rename_job({
\ 'cmd': cmd,
\ 'bang': a:bang,

View File

@ -0,0 +1,9 @@
package complete
type T struct {
V string
}
func Example(s string) {
Example("")
}

View File

@ -26,19 +26,11 @@ function! go#test#Test(bang, compile, ...) abort
call add(args, printf("-timeout=%s", timeout))
endif
if go#config#EchoCommandInfo()
if a:compile
call go#util#EchoProgress("compiling tests ...")
else
call go#util#EchoProgress("testing...")
endif
endif
if has('nvim') && go#config#TermEnabled()
call go#term#new(a:bang, ["go"] + args)
endif
if go#util#has_job() || has('nvim')
if go#util#has_job()
" use vim's job functionality to call it asynchronously
let job_options = {
\ 'bang': a:bang,
@ -55,6 +47,14 @@ function! go#test#Test(bang, compile, ...) abort
return
endif
if go#config#EchoCommandInfo()
if a:compile
call go#util#EchoProgress("compiling tests ...")
else
call go#util#EchoProgress("testing...")
endif
endif
call go#cmd#autowrite()
redraw
@ -152,12 +152,12 @@ function! s:errorformat() abort
" each level of test indents the test output 4 spaces. Capturing groups
" (e.g. \(\)) cannot be used in an errorformat, but non-capturing groups can
" (e.g. \%(\)).
let indent = '%\\%( %\\)%#'
let indent = '%\\%( %\\)'
" ignore `go test -v` output for starting tests
let format = "%-G=== RUN %.%#"
" ignore `go test -v` output for passing tests
let format .= ",%-G" . indent . "--- PASS: %.%#"
let format .= ",%-G" . indent . "%#--- PASS: %.%#"
" Match failure lines.
"
@ -167,24 +167,25 @@ function! s:errorformat() abort
" e.g.:
" '--- FAIL: TestSomething (0.00s)'
if show_name
let format .= ",%G" . indent . "--- FAIL: %m (%.%#)"
let format .= ",%G" . indent . "%#--- FAIL: %m (%.%#)"
else
let format .= ",%-G" . indent . "--- FAIL: %.%#"
let format .= ",%-G" . indent . "%#--- FAIL: %.%#"
endif
" Go 1.10 test output {{{1
" Matches test output lines.
"
" All test output lines start with the test indentation and a tab, followed
" by the filename, a colon, the line number, another colon, a space, and the
" message. e.g.:
" '\ttime_test.go:30: Likely problem: the time zone files have not been installed.'
let format .= ",%A" . indent . "%\\t%\\+%f:%l: %m"
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: %m"
" also match lines that don't have a message (i.e. the message begins with a
" newline or is the empty string):
" e.g.:
" t.Errorf("\ngot %v; want %v", actual, expected)
" t.Error("")
let format .= ",%A" . indent . "%\\t%\\+%f:%l: "
let format .= ",%A" . indent . "%#%\\t%\\+%f:%l: "
" Match the 2nd and later lines of multi-line output. These lines are
" indented the number of spaces for the level of nesting of the test,
@ -197,7 +198,17 @@ function! s:errorformat() abort
" indicate that they're multiple lines of output, but in that case the lines
" get concatenated in the quickfix list, which is not what users typically
" want when writing a newline into their test output.
let format .= ",%G" . indent . "%\\t%\\{2}%m"
let format .= ",%G" . indent . "%#%\\t%\\{2}%m"
" }}}1
" Go 1.11 test output {{{1
" Match test output lines similarly to Go 1.10 test output lines, but they
" use an indent level where the Go 1.10 test output uses tabs, so they'll
" always have at least one level indentation...
let format .= ",%A" . indent . "%\\+%f:%l: %m"
let format .= ",%A" . indent . "%\\+%f:%l: "
let format .= ",%G" . indent . "%\\{2\\,}%m"
" }}}1
" set the format for panics.
@ -261,16 +272,16 @@ function! s:errorformat() abort
let format .= ",%-Cexit status %[0-9]%\\+"
"let format .= ",exit status %[0-9]%\\+"
" Match and ignore exit failure lines whether part of a multi-line message
" Match and ignore failure lines whether part of a multi-line message
" or not, because these lines sometimes come before and sometimes after
" panic stacktraces.
let format .= ",%-CFAIL%\\t%.%#"
"let format .= ",FAIL%\\t%.%#"
" match compiler errors
" These are very smilar to errors from test output, but lack leading tabs
" for the first line of an error, and subsequent lines only have one tab
" instead of two.
" match compiler errors.
" These are very smilar to errors from <=go1.10 test output, but lack
" leading tabs for the first line of an error, and subsequent lines only
" have one tab instead of two.
let format .= ",%A%f:%l:%c: %m"
let format .= ",%A%f:%l: %m"
" It would be nice if this weren't necessary, but panic lines from tests are

View File

@ -74,7 +74,7 @@ endfunc
func! Test_GoTestVet() abort
let expected = [
\ {'lnum': 6, 'bufnr': 16, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has only 0 args'},
\ {'lnum': 6, 'bufnr': 16, 'col': 0, 'valid': 1, 'vcol': 0, 'nr': -1, 'type': '', 'pattern': '', 'text': 'Errorf format %v reads arg #1, but call has 0 args'},
\ ]
call s:test('veterror/veterror.go', expected)
endfunc

View File

@ -76,12 +76,12 @@ function! go#tool#Imports() abort
return imports
endfunction
function! go#tool#Info() abort
function! go#tool#Info(showstatus) abort
let l:mode = go#config#InfoMode()
if l:mode == 'gocode'
call go#complete#Info()
call go#complete#Info(a:showstatus)
elseif l:mode == 'guru'
call go#guru#DescribeInfo()
call go#guru#DescribeInfo(a:showstatus)
else
call go#util#EchoError('go_info_mode value: '. l:mode .' is not valid. Valid values are: [gocode, guru]')
endif

View File

@ -64,6 +64,10 @@ endfunction
" The (optional) first parameter can be added to indicate the 'cwd' or 'env'
" parameters will be used, which wasn't added until a later version.
function! go#util#has_job(...) abort
if has('nvim')
return 1
endif
" cwd and env parameters to job_start was added in this version.
if a:0 > 0 && a:1 is 1
return has('job') && has("patch-8.0.0902")