mirror of
				https://github.com/amix/vimrc
				synced 2025-10-31 14:43:35 +08:00 
			
		
		
		
	Updated plugins
This commit is contained in:
		| @ -117,7 +117,7 @@ function! ctrlp#decls#enter() abort | ||||
|     call add(s:decls, printf("%s\t%s |%s:%s:%s|\t%s", | ||||
|           \ decl.ident . space, | ||||
|           \ decl.keyword, | ||||
|           \ fnamemodify(decl.filename, ":t"), | ||||
|           \ fnamemodify(decl.filename, ":."), | ||||
|           \ decl.line, | ||||
|           \ decl.col, | ||||
|           \ decl.full, | ||||
|  | ||||
| @ -30,8 +30,8 @@ function! go#cmd#Build(bang, ...) abort | ||||
|         \ map(copy(a:000), "expand(v:val)") + | ||||
|         \ [".", "errors"] | ||||
|  | ||||
|   " Vim async. | ||||
|   if go#util#has_job() | ||||
|   " Vim and Neovim async. | ||||
|   if go#util#has_job() || has('nvim') | ||||
|     if go#config#EchoCommandInfo() | ||||
|       call go#util#EchoProgress("building dispatched ...") | ||||
|     endif | ||||
| @ -42,14 +42,6 @@ function! go#cmd#Build(bang, ...) abort | ||||
|           \ 'for': 'GoBuild', | ||||
|           \}) | ||||
|  | ||||
|   " Nvim async. | ||||
|   elseif has('nvim') | ||||
|     if go#config#EchoCommandInfo() | ||||
|       call go#util#EchoProgress("building dispatched ...") | ||||
|     endif | ||||
|  | ||||
|     call go#jobcontrol#Spawn(a:bang, "build", "GoBuild", args) | ||||
|  | ||||
|   " Vim 7.4 without async | ||||
|   else | ||||
|     let default_makeprg = &makeprg | ||||
| @ -297,44 +289,7 @@ function s:cmd_job(args) abort | ||||
|   " autowrite is not enabled for jobs | ||||
|   call go#cmd#autowrite() | ||||
|  | ||||
|   function! s:complete(job, exit_status, data) closure abort | ||||
|     let status = { | ||||
|           \ 'desc': 'last status', | ||||
|           \ 'type': a:args.cmd[1], | ||||
|           \ 'state': "success", | ||||
|           \ } | ||||
|  | ||||
|     if a:exit_status | ||||
|       let status.state = "failed" | ||||
|     endif | ||||
|  | ||||
|     let elapsed_time = reltimestr(reltime(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) | ||||
|   endfunction | ||||
|  | ||||
|   let a:args.complete = funcref('s:complete') | ||||
|   let callbacks = go#job#Spawn(a:args) | ||||
|  | ||||
|   let start_options = { | ||||
|         \ 'callback': callbacks.callback, | ||||
|         \ 'exit_cb': callbacks.exit_cb, | ||||
|         \ 'close_cb': callbacks.close_cb, | ||||
|         \ } | ||||
|  | ||||
|   " pre start | ||||
|   let dir = getcwd() | ||||
|   let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|   let jobdir = fnameescape(expand("%:p:h")) | ||||
|   execute cd . jobdir | ||||
|  | ||||
|   call job_start(a:args.cmd, start_options) | ||||
|  | ||||
|   " post start | ||||
|   execute cd . fnameescape(dir) | ||||
|   call go#job#Spawn(a:args.cmd, a:args) | ||||
| endfunction | ||||
|  | ||||
| " vim: sw=2 ts=2 et | ||||
|  | ||||
| @ -48,12 +48,13 @@ function! go#coverage#Buffer(bang, ...) abort | ||||
|     call go#util#EchoProgress("testing...") | ||||
|   endif | ||||
|  | ||||
|   if go#util#has_job() | ||||
|   if go#util#has_job() || has('nvim') | ||||
|     call s:coverage_job({ | ||||
|           \ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname] + a:000, | ||||
|           \ 'complete': function('s:coverage_callback', [l:tmpname]), | ||||
|           \ 'bang': a:bang, | ||||
|           \ 'for': 'GoTest', | ||||
|           \ 'statustype': 'coverage', | ||||
|           \ }) | ||||
|     return | ||||
|   endif | ||||
| @ -63,24 +64,8 @@ function! go#coverage#Buffer(bang, ...) abort | ||||
|     call extend(args, a:000) | ||||
|   endif | ||||
|  | ||||
|   let disabled_term = 0 | ||||
|   if go#config#TermEnabled() | ||||
|     let disabled_term = 1 | ||||
|     call go#config#SetTermEnabled(0) | ||||
|   endif | ||||
|  | ||||
|   let id = call('go#test#Test', args) | ||||
|  | ||||
|   if disabled_term | ||||
|     call go#config#SetTermEnabled(1) | ||||
|   endif | ||||
|  | ||||
|   if has('nvim') | ||||
|     call go#jobcontrol#AddHandler(function('s:coverage_handler')) | ||||
|     let s:coverage_handler_jobs[id] = l:tmpname | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   if go#util#ShellError() == 0 | ||||
|     call go#coverage#overlay(l:tmpname) | ||||
|   endif | ||||
| @ -104,7 +89,7 @@ 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() | ||||
|   if go#util#has_job() || has('nvim') | ||||
|     call s:coverage_job({ | ||||
|           \ 'cmd': ['go', 'test', '-tags', go#config#BuildTags(), '-coverprofile', l:tmpname], | ||||
|           \ 'complete': function('s:coverage_browser_callback', [l:tmpname]), | ||||
| @ -120,11 +105,6 @@ function! go#coverage#Browser(bang, ...) abort | ||||
|   endif | ||||
|  | ||||
|   let id = call('go#test#Test', args) | ||||
|   if has('nvim') | ||||
|     call go#jobcontrol#AddHandler(function('s:coverage_browser_handler')) | ||||
|     let s:coverage_browser_handler_jobs[id] = l:tmpname | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   if go#util#ShellError() == 0 | ||||
|     call go#tool#ExecuteInDir(['go', 'tool', 'cover', '-html=' . l:tmpname]) | ||||
| @ -275,48 +255,17 @@ function s:coverage_job(args) | ||||
|   " autowrite is not enabled for jobs | ||||
|   call go#cmd#autowrite() | ||||
|  | ||||
|   let status_dir =  expand('%:p:h') | ||||
|   let Complete = a:args.complete | ||||
|   function! s:complete(job, exit_status, data) closure | ||||
|     let status = { | ||||
|           \ 'desc': 'last status', | ||||
|           \ 'type': "coverage", | ||||
|           \ 'state': "finished", | ||||
|           \ } | ||||
|   let disabled_term = 0 | ||||
|   if go#config#TermEnabled() | ||||
|     let disabled_term = 1 | ||||
|     call go#config#SetTermEnabled(0) | ||||
|   endif | ||||
|  | ||||
|     if a:exit_status | ||||
|       let status.state = "failed" | ||||
|     endif | ||||
|   call go#job#Spawn(a:args.cmd, a:args) | ||||
|  | ||||
|     call go#statusline#Update(status_dir, status) | ||||
|     return Complete(a:job, a:exit_status, a:data) | ||||
|   endfunction | ||||
|  | ||||
|   let a:args.complete = funcref('s:complete') | ||||
|   let callbacks = go#job#Spawn(a:args) | ||||
|  | ||||
|   let start_options = { | ||||
|         \ 'callback': callbacks.callback, | ||||
|         \ 'exit_cb': callbacks.exit_cb, | ||||
|         \ 'close_cb': callbacks.close_cb, | ||||
|         \ } | ||||
|  | ||||
|   " pre start | ||||
|   let dir = getcwd() | ||||
|   let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|   let jobdir = fnameescape(expand("%:p:h")) | ||||
|   execute cd . jobdir | ||||
|  | ||||
|   call go#statusline#Update(status_dir, { | ||||
|         \ 'desc': "current status", | ||||
|         \ 'type': "coverage", | ||||
|         \ 'state': "started", | ||||
|         \}) | ||||
|  | ||||
|   call job_start(a:args.cmd, start_options) | ||||
|  | ||||
|   " post start | ||||
|   execute cd . fnameescape(dir) | ||||
|   if disabled_term | ||||
|     call go#config#SetTermEnabled(1) | ||||
|   endif | ||||
| endfunction | ||||
|  | ||||
| " coverage_callback is called when the coverage execution is finished | ||||
| @ -336,39 +285,4 @@ function! s:coverage_browser_callback(coverfile, job, exit_status, data) | ||||
|   call delete(a:coverfile) | ||||
| endfunction | ||||
|  | ||||
| " ----------------------- | ||||
| " | Neovim job handlers | | ||||
| " ----------------------- | ||||
|  | ||||
| let s:coverage_handler_jobs = {} | ||||
| let s:coverage_browser_handler_jobs = {} | ||||
|  | ||||
| function! s:coverage_handler(job, exit_status, data) abort | ||||
|   if !has_key(s:coverage_handler_jobs, a:job.id) | ||||
|     return | ||||
|   endif | ||||
|   let l:tmpname = s:coverage_handler_jobs[a:job.id] | ||||
|   if a:exit_status == 0 | ||||
|     call go#coverage#overlay(l:tmpname) | ||||
|   endif | ||||
|  | ||||
|   call delete(l:tmpname) | ||||
|   unlet s:coverage_handler_jobs[a:job.id] | ||||
| endfunction | ||||
|  | ||||
| function! s:coverage_browser_handler(job, exit_status, data) abort | ||||
|   if !has_key(s:coverage_browser_handler_jobs, a:job.id) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   let l:tmpname = s:coverage_browser_handler_jobs[a:job.id] | ||||
|   if a:exit_status == 0 | ||||
|     call go#tool#ExecuteInDir(['go', 'tool', 'cover', '-html=' . l:tmpname]) | ||||
|   endif | ||||
|  | ||||
|   call delete(l:tmpname) | ||||
|   unlet s:coverage_browser_handler_jobs[a:job.id] | ||||
| endfunction | ||||
|  | ||||
|  | ||||
| " vim: sw=2 ts=2 et | ||||
|  | ||||
| @ -26,7 +26,12 @@ function! go#def#Jump(mode) abort | ||||
|     endif | ||||
|  | ||||
|   elseif bin_name == 'guru' | ||||
|     let cmd = [bin_name, '-tags', go#config#BuildTags()] | ||||
|     let cmd = [go#path#CheckBinPath(bin_name)] | ||||
|     let buildtags = go#config#BuildTags() | ||||
|     if buildtags isnot '' | ||||
|       let cmd += ['-tags', buildtags] | ||||
|     endif | ||||
|  | ||||
|     let stdin_content = "" | ||||
|  | ||||
|     if &modified | ||||
| @ -278,13 +283,7 @@ function! go#def#Stack(...) abort | ||||
| endfunction | ||||
|  | ||||
| function s:def_job(args) abort | ||||
|   let callbacks = go#job#Spawn(a:args) | ||||
|  | ||||
|   let start_options = { | ||||
|         \ 'callback': callbacks.callback, | ||||
|         \ 'exit_cb': callbacks.exit_cb, | ||||
|         \ 'close_cb': callbacks.close_cb, | ||||
|         \ } | ||||
|   let l:start_options = go#job#Options(a:args) | ||||
|  | ||||
|   if &modified | ||||
|     let l:tmpname = tempname() | ||||
| @ -293,7 +292,7 @@ function s:def_job(args) abort | ||||
|     let l:start_options.in_name = l:tmpname | ||||
|   endif | ||||
|  | ||||
|   call job_start(a:args.cmd, start_options) | ||||
|   call go#job#Start(a:args.cmd, start_options) | ||||
| endfunction | ||||
|  | ||||
| " vim: sw=2 ts=2 et | ||||
|  | ||||
| @ -101,13 +101,29 @@ function! s:root_dirs() abort | ||||
|   return dirs | ||||
| endfunction | ||||
|  | ||||
| function! s:go_packages(dirs) abort | ||||
| function! s:go_packages(dirs, arglead) abort | ||||
|   let pkgs = [] | ||||
|   for d in a:dirs | ||||
|     let pkg_root = expand(d . '/pkg/' . go#util#osarch()) | ||||
|     call extend(pkgs, split(globpath(pkg_root, '**/*.a', 1), "\n")) | ||||
|   for dir in a:dirs | ||||
|       " this may expand to multiple lines | ||||
|       let scr_root = expand(dir . '/src/') | ||||
|       for pkg in split(globpath(scr_root, a:arglead.'*'), "\n") | ||||
|           if isdirectory(pkg) | ||||
|               let pkg .= '/' | ||||
|           elseif pkg !~ '\.a$' | ||||
|               continue | ||||
|           endif | ||||
|  | ||||
|           " without this the result can have duplicates in form of | ||||
|           " 'encoding/json' and '/encoding/json/' | ||||
|           let pkg = go#util#StripPathSep(pkg) | ||||
|  | ||||
|           " remove the scr root and keep the package in tact | ||||
|           let pkg = substitute(pkg, scr_root, "", "") | ||||
|           call add(pkgs, pkg) | ||||
|       endfor | ||||
|   endfor | ||||
|   return map(pkgs, "fnamemodify(v:val, ':t:r')") | ||||
|  | ||||
|   return pkgs | ||||
| endfunction | ||||
|  | ||||
| function! s:interface_list(pkg) abort | ||||
| @ -124,13 +140,24 @@ endfunction | ||||
| " Complete package and interface for {interface} | ||||
| function! go#impl#Complete(arglead, cmdline, cursorpos) abort | ||||
|   let words = split(a:cmdline, '\s\+', 1) | ||||
|  | ||||
|   if words[-1] ==# '' | ||||
|     return s:uniq(sort(s:go_packages(s:root_dirs()))) | ||||
|   elseif words[-1] =~# '^\h\w*$' | ||||
|     return s:uniq(sort(filter(s:go_packages(s:root_dirs()), 'stridx(v:val, words[-1]) == 0'))) | ||||
|   elseif words[-1] =~# '^\h\w*\.\%(\h\w*\)\=$' | ||||
|     let [pkg, interface] = split(words[-1], '\.', 1) | ||||
|     echomsg pkg | ||||
|     " if no words are given, just start completing the first package we found | ||||
|     return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead))) | ||||
|   elseif words[-1] =~# '^\(\h\w.*\.\%(\h\w*\)\=$\)\@!\S*$' | ||||
|     " start matching go packages. It's negate match of the below match | ||||
|     return s:uniq(sort(s:go_packages(s:root_dirs(), a:arglead))) | ||||
|   elseif words[-1] =~# '^\h\w.*\.\%(\h\w*\)\=$' | ||||
|     " match the following, anything that could indicate an interface candidate | ||||
|     "  | ||||
|     "  io. | ||||
|     "  io.Wr | ||||
|     "  github.com/fatih/color. | ||||
|     "  github.com/fatih/color.U | ||||
|     "  github.com/fatih/color.Un | ||||
|     let splitted = split(words[-1], '\.', 1) | ||||
|     let pkg = join(splitted[:-2], '.') | ||||
|     let interface = splitted[-1] | ||||
|     return s:uniq(sort(filter(s:interface_list(pkg), 'v:val =~? words[-1]'))) | ||||
|   else | ||||
|     return [] | ||||
|  | ||||
| @ -1,7 +1,25 @@ | ||||
| " Spawn returns callbacks to be used with job_start. It is abstracted to be | ||||
| " Spawn starts an asynchronous job. See the description of go#job#Options to | ||||
| " understand the args parameter. | ||||
| " | ||||
| " Spawn returns a job. | ||||
| function! go#job#Spawn(cmd, args) | ||||
|   let l:options = go#job#Options(a:args) | ||||
|   return go#job#Start(a:cmd, l:options) | ||||
| endfunction | ||||
|  | ||||
| " Spawn starts an asynchronous job. See the description of go#job#Options to | ||||
| " understand the args parameter. | ||||
| " | ||||
| " Spawn returns a job. | ||||
| function! go#job#Spawn(cmd, args) | ||||
|   let l:options = go#job#Options(a:args) | ||||
|   return go#job#Start(a:cmd, l:options) | ||||
| endfunction | ||||
|  | ||||
| " Options returns callbacks to be used with job_start. It is abstracted to be | ||||
| " used with various go commands, such as build, test, install, etc.. This | ||||
| " allows us to avoid writing the same callback over and over for some | ||||
| " commands. It's fully customizable so each command can change it to it's own | ||||
| " commands. It's fully customizable so each command can change it to its own | ||||
| " logic. | ||||
| " | ||||
| " args is a dictionary with the these keys: | ||||
| @ -10,9 +28,16 @@ | ||||
| "   'bang': | ||||
| "     Set to 0 to jump to the first error in the error list. | ||||
| "     Defaults to 0. | ||||
| "   'statustype': | ||||
| "     The status type to use when updating the status. | ||||
| "     See statusline.vim. | ||||
| "   'for': | ||||
| "     The g:go_list_type_command key to use to get the error list type to use. | ||||
| "     Defaults to '_job' | ||||
| "   'errorformat': | ||||
| "     The errorformat string to use when parsing errors. Defaults to | ||||
| "     &errorformat. | ||||
| "     See :help 'errorformat'. | ||||
| "   'complete': | ||||
| "     A function to call after the job exits and the channel is closed. The | ||||
| "     function will be passed three arguments: the job, its exit code, and the | ||||
| @ -30,22 +55,28 @@ | ||||
| "   'close_cb': | ||||
| "     A function suitable to be passed as a job close_cb handler. See | ||||
| "     job-close_cb. | ||||
| function go#job#Spawn(args) | ||||
| "   'cwd': | ||||
| "     The path to the directory which contains the current buffer. | ||||
| function! go#job#Options(args) | ||||
|   let cbs = {} | ||||
|   let state = { | ||||
|         \ 'winid': win_getid(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 | ||||
|         \ 'errorformat': &errorformat, | ||||
|         \ 'statustype' : '' | ||||
|       \ } | ||||
|  | ||||
|   if has("patch-8.0.0902") || has('nvim') | ||||
|     let cbs.cwd = state.jobdir | ||||
|   endif | ||||
|  | ||||
|   if has_key(a:args, 'bang') | ||||
|     let state.bang = a:args.bang | ||||
|   endif | ||||
| @ -54,14 +85,78 @@ function go#job#Spawn(args) | ||||
|     let state.for = a:args.for | ||||
|   endif | ||||
|  | ||||
|   if has_key(a:args, 'statustype') | ||||
|     let state.statustype = a:args.statustype | ||||
|   endif | ||||
|  | ||||
|   if has_key(a:args, 'errorformat') | ||||
|     let state.errorformat = a:args.errorformat | ||||
|   endif | ||||
|  | ||||
|   " do nothing in state.complete by default. | ||||
|   function state.complete(job, exit_status, data) | ||||
|   endfunction | ||||
|  | ||||
|   function state.show_status(job, exit_status) dict | ||||
|     if go#config#EchoCommandInfo() | ||||
|       let prefix = "" | ||||
|       if self.statustype != '' | ||||
|         let prefix = '[' . self.statustype . '] ' | ||||
|       endif | ||||
|       if a:exit_status == 0 | ||||
|         call go#util#EchoSuccess(prefix . "SUCCESS") | ||||
|       else | ||||
|         call go#util#EchoError(prefix . "FAIL") | ||||
|       endif | ||||
|     endif | ||||
|  | ||||
|     if self.statustype == '' | ||||
|       return | ||||
|     endif | ||||
|  | ||||
|     let status = { | ||||
|           \ 'desc': 'last status', | ||||
|           \ 'type': self.statustype, | ||||
|           \ 'state': "success", | ||||
|           \ } | ||||
|  | ||||
|     if a:exit_status | ||||
|       let status.state = "failed" | ||||
|     endif | ||||
|  | ||||
|     if has_key(self, '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) | ||||
|     endif | ||||
|  | ||||
|     call go#statusline#Update(self.jobdir, status) | ||||
|   endfunction | ||||
|  | ||||
|   if has_key(a:args, 'complete') | ||||
|     let state.complete = a:args.complete | ||||
|   endif | ||||
|  | ||||
|   function! s:start(args) dict | ||||
|     if self.statustype != '' | ||||
|       let status = { | ||||
|             \ 'desc': 'current status', | ||||
|             \ 'type': self.statustype, | ||||
|             \ 'state': "started", | ||||
|             \ } | ||||
|  | ||||
|       call go#statusline#Update(self.jobdir, status) | ||||
|     endif | ||||
|     let self.started_at = reltime() | ||||
|   endfunction | ||||
|   " explicitly bind _start to state so that within it, self will | ||||
|   " always refer to state. See :help Partial for more information. | ||||
|   " | ||||
|   " _start is intended only for internal use and should not be referenced | ||||
|   " outside of this file. | ||||
|   let cbs._start = function('s:start', [''], state) | ||||
|  | ||||
|   function! s:callback(chan, msg) dict | ||||
|     call add(self.messages, a:msg) | ||||
|   endfunction | ||||
| @ -73,15 +168,9 @@ function go#job#Spawn(args) | ||||
|     let self.exit_status = a:exitval | ||||
|     let self.exited = 1 | ||||
|  | ||||
|     if go#config#EchoCommandInfo() | ||||
|       if a:exitval == 0 | ||||
|         call go#util#EchoSuccess("SUCCESS") | ||||
|       else | ||||
|         call go#util#EchoError("FAILED") | ||||
|       endif | ||||
|     endif | ||||
|     call self.show_status(a:job, a:exitval) | ||||
|  | ||||
|     if self.closed | ||||
|     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 | ||||
| @ -123,14 +212,14 @@ function go#job#Spawn(args) | ||||
|  | ||||
|     let out = join(self.messages, "\n") | ||||
|  | ||||
|     let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|     let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' | ||||
|     try | ||||
|       " parse the errors relative to self.jobdir | ||||
|       execute cd self.jobdir | ||||
|       execute l: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(self.dir) | ||||
|       execute l:cd fnameescape(self.dir) | ||||
|     endtry | ||||
|  | ||||
|  | ||||
| @ -149,7 +238,178 @@ function go#job#Spawn(args) | ||||
|     endif | ||||
|   endfunction | ||||
|  | ||||
|   if has('nvim') | ||||
|     return s:neooptions(cbs) | ||||
|   endif | ||||
|  | ||||
|   return cbs | ||||
| endfunction | ||||
|  | ||||
| function! go#job#Start(cmd, options) | ||||
|   let l:cd = exists('*haslocaldir') && haslocaldir() ? 'lcd' : 'cd' | ||||
|   let l:options = copy(a:options) | ||||
|  | ||||
|   if !has_key(l:options, 'cwd') | ||||
|     " pre start | ||||
|     let dir = getcwd() | ||||
|     execute l:cd fnameescape(expand("%:p:h")) | ||||
|   endif | ||||
|  | ||||
|   if has_key(l:options, '_start') | ||||
|     call l:options._start() | ||||
|     " remove _start to play nicely with vim (when vim encounters an unexpected | ||||
|     " job option it reports an "E475: invalid argument" error. | ||||
|     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) | ||||
|     endif | ||||
|  | ||||
|     let job = jobstart(a:cmd, l:options) | ||||
|  | ||||
|     if len(l:input) > 0 | ||||
|       call chansend(job, l:input) | ||||
|       " close stdin to signal that no more bytes will be sent. | ||||
|       call chanclose(job, 'stdin') | ||||
|     endif | ||||
|   else | ||||
|     let job = job_start(a:cmd, l:options) | ||||
|   endif | ||||
|  | ||||
|   if !has_key(l:options, 'cwd') | ||||
|     " post start | ||||
|     execute l:cd fnameescape(dir) | ||||
|   endif | ||||
|  | ||||
|   return job | ||||
| endfunction | ||||
|  | ||||
| " s:neooptions returns a dictionary of job options suitable for use by Neovim | ||||
| " based on a dictionary of job options suitable for Vim8. | ||||
| function! s:neooptions(options) | ||||
|   let l:options = {} | ||||
|   let l:options['stdout_buf'] = '' | ||||
|   let l:options['stderr_buf'] = '' | ||||
|  | ||||
|   for key in keys(a:options) | ||||
|       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 = "" | ||||
|  | ||||
|             if l:data[-1] != "" | ||||
|               let self.stdout_buf = l:data[-1] | ||||
|             endif | ||||
|  | ||||
|             let l:data = l:data[:-2] | ||||
|             if len(l:data) == 0 | ||||
|               return | ||||
|             endif | ||||
|  | ||||
|             call self.callback(a:ch, join(l:data, "\n")) | ||||
|           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 = "" | ||||
|  | ||||
|             if l:data[-1] != "" | ||||
|               let self.stderr_buf = l:data[-1] | ||||
|             endif | ||||
|  | ||||
|             let l:data = l:data[:-2] | ||||
|             if len(l:data) == 0 | ||||
|               return | ||||
|             endif | ||||
|  | ||||
|             call self.callback(a:ch, join(l:data, "\n")) | ||||
|           endfunction | ||||
|           let l:options['on_stderr'] = function('s:callback2on_stderr', [], l:options) | ||||
|         endif | ||||
|  | ||||
|         continue | ||||
|       endif | ||||
|  | ||||
|       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 = "" | ||||
|  | ||||
|           if l:data[-1] != "" | ||||
|             let self.stdout_buf = l:data[-1] | ||||
|           endif | ||||
|  | ||||
|           let l:data = l:data[:-2] | ||||
|           if len(l:data) == 0 | ||||
|             return | ||||
|           endif | ||||
|  | ||||
|           call self.out_cb(a:ch, join(l:data, "\n")) | ||||
|         endfunction | ||||
|         let l:options['on_stdout'] = function('s:on_stdout', [], l:options) | ||||
|  | ||||
|         continue | ||||
|       endif | ||||
|  | ||||
|       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 = "" | ||||
|  | ||||
|           if l:data[-1] != "" | ||||
|             let self.stderr_buf = l:data[-1] | ||||
|           endif | ||||
|  | ||||
|           let l:data = l:data[:-2] | ||||
|           if len(l:data) == 0 | ||||
|             return | ||||
|           endif | ||||
|  | ||||
|           call self.err_cb(a:ch, join(l:data, "\n")) | ||||
|         endfunction | ||||
|         let l:options['on_stderr'] = function('s:on_stderr', [], l:options) | ||||
|  | ||||
|         continue | ||||
|       endif | ||||
|  | ||||
|       if key == 'exit_cb' | ||||
|         let l:options['exit_cb'] = a:options['exit_cb'] | ||||
|         function! s:on_exit(jobid, exitval, event) dict | ||||
|           call self.exit_cb(a:jobid, a:exitval) | ||||
|         endfunction | ||||
|         let l:options['on_exit'] = function('s:on_exit', [], l:options) | ||||
|  | ||||
|         continue | ||||
|       endif | ||||
|  | ||||
|       if key == 'close_cb' | ||||
|         continue | ||||
|       endif | ||||
|  | ||||
|   endfor | ||||
|   return l:options | ||||
| endfunction | ||||
|  | ||||
|  | ||||
| " vim: sw=2 ts=2 et | ||||
|  | ||||
| @ -1,195 +0,0 @@ | ||||
| " s:jobs is a global reference to all jobs started with Spawn() or with the | ||||
| " internal function s:spawn | ||||
| let s:jobs = {} | ||||
|  | ||||
| " s:handlers is a global event handlers for all jobs started with Spawn() or | ||||
| " with the internal function s:spawn | ||||
| let s:handlers = {} | ||||
|  | ||||
| " Spawn is a wrapper around s:spawn. It can be executed by other files and | ||||
| " scripts if needed. Desc defines the description for printing the status | ||||
| " during the job execution (useful for statusline integration). | ||||
| function! go#jobcontrol#Spawn(bang, desc, for, args) abort | ||||
|   " autowrite is not enabled for jobs | ||||
|   call go#cmd#autowrite() | ||||
|  | ||||
|   let job = s:spawn(a:bang, a:desc, a:for, a:args) | ||||
|   return job.id | ||||
| endfunction | ||||
|  | ||||
| " AddHandler adds a on_exit callback handler and returns the id. | ||||
| function! go#jobcontrol#AddHandler(handler) abort | ||||
|   let i = len(s:handlers) | ||||
|   while has_key(s:handlers, string(i)) | ||||
|     let i += 1 | ||||
|     break | ||||
|   endwhile | ||||
|   let s:handlers[string(i)] = a:handler | ||||
|   return string(i) | ||||
| endfunction | ||||
|  | ||||
| " RemoveHandler removes a callback handler by id. | ||||
| function! go#jobcontrol#RemoveHandler(id) abort | ||||
|   unlet s:handlers[a:id] | ||||
| endfunction | ||||
|  | ||||
| " spawn spawns a go subcommand with the name and arguments with jobstart. Once a | ||||
| " job is started a reference will be stored inside s:jobs. The job is started | ||||
| " inside the current files folder. | ||||
| function! s:spawn(bang, desc, for, args) abort | ||||
|   let status_type = a:args[0] | ||||
|   let status_dir = expand('%:p:h') | ||||
|   let started_at = reltime() | ||||
|  | ||||
|   call go#statusline#Update(status_dir, { | ||||
|         \ 'desc': "current status", | ||||
|         \ 'type': status_type, | ||||
|         \ 'state': "started", | ||||
|         \}) | ||||
|  | ||||
|   let job = { | ||||
|         \ 'desc': a:desc, | ||||
|         \ 'bang': a:bang, | ||||
|         \ 'winid': win_getid(winnr()), | ||||
|         \ 'importpath': go#package#ImportPath(), | ||||
|         \ 'state': "RUNNING", | ||||
|         \ 'stderr' : [], | ||||
|         \ 'stdout' : [], | ||||
|         \ 'on_stdout': function('s:on_stdout'), | ||||
|         \ 'on_stderr': function('s:on_stderr'), | ||||
|         \ 'on_exit' : function('s:on_exit'), | ||||
|         \ 'status_type' : status_type, | ||||
|         \ 'status_dir' : status_dir, | ||||
|         \ 'started_at' : started_at, | ||||
|         \ 'for' : a:for, | ||||
|         \ 'errorformat': &errorformat, | ||||
|         \ } | ||||
|  | ||||
|   " execute go build in the files directory | ||||
|   let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|  | ||||
|   " cleanup previous jobs for this file | ||||
|   for jb in values(s:jobs) | ||||
|     if jb.importpath == job.importpath | ||||
|       unlet s:jobs[jb.id] | ||||
|     endif | ||||
|   endfor | ||||
|  | ||||
|   let dir = getcwd() | ||||
|   let jobdir = fnameescape(expand("%:p:h")) | ||||
|   execute cd . jobdir | ||||
|  | ||||
|   " append the subcommand, such as 'build' | ||||
|   let argv = ['go'] + a:args | ||||
|  | ||||
|   " run, forrest, run! | ||||
|   let id = jobstart(argv, job) | ||||
|   let job.id = id | ||||
|   let job.dir = jobdir | ||||
|   let s:jobs[id] = job | ||||
|  | ||||
|   execute cd . fnameescape(dir) | ||||
|  | ||||
|   return job | ||||
| endfunction | ||||
|  | ||||
| " on_exit is the exit handler for jobstart(). It handles cleaning up the job | ||||
| " references and also displaying errors in the quickfix window collected by | ||||
| " on_stderr handler. If there are no errors and a quickfix window is open, | ||||
| " it'll be closed. | ||||
| function! s:on_exit(job_id, exit_status, event) dict abort | ||||
|   let l:winid = win_getid(winnr()) | ||||
|   call win_gotoid(self.winid) | ||||
|  | ||||
|   let status = { | ||||
|         \ 'desc': 'last status', | ||||
|         \ 'type': self.status_type, | ||||
|         \ 'state': "success", | ||||
|         \ } | ||||
|  | ||||
|   if a:exit_status | ||||
|     let status.state = "failed" | ||||
|   endif | ||||
|  | ||||
|   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(self.status_dir, status) | ||||
|  | ||||
|   let std_combined = self.stderr + self.stdout | ||||
|  | ||||
|   let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|   let dir = getcwd() | ||||
|   execute cd self.dir | ||||
|  | ||||
|   call s:callback_handlers_on_exit(s:jobs[a:job_id], a:exit_status, std_combined) | ||||
|  | ||||
|   let l:listtype = go#list#Type(self.for) | ||||
|   if a:exit_status == 0 | ||||
|     call go#list#Clean(l:listtype) | ||||
|  | ||||
|     let self.state = "SUCCESS" | ||||
|  | ||||
|     if go#config#EchoCommandInfo() | ||||
|       call go#util#EchoSuccess("[" . self.status_type . "] SUCCESS") | ||||
|     endif | ||||
|  | ||||
|     execute cd . fnameescape(dir) | ||||
|     call win_gotoid(l:winid) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   let self.state = "FAILED" | ||||
|  | ||||
|   if go#config#EchoCommandInfo() | ||||
|     call go#util#EchoError("[" . self.status_type . "] FAILED") | ||||
|   endif | ||||
|  | ||||
|   " 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) | ||||
|  | ||||
|   if !len(errors) | ||||
|     " failed to parse errors, output the original content | ||||
|     call go#util#EchoError(std_combined[0]) | ||||
|     call win_gotoid(l:winid) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   " if we are still in the same windows show the list | ||||
|   if self.winid == l:winid | ||||
|     call go#list#Window(l:listtype, len(errors)) | ||||
|     if !empty(errors) && !self.bang | ||||
|       call go#list#JumpToFirst(l:listtype) | ||||
|     endif | ||||
|   endif | ||||
| endfunction | ||||
|  | ||||
| " callback_handlers_on_exit runs all handlers for job on exit event. | ||||
| function! s:callback_handlers_on_exit(job, exit_status, data) abort | ||||
|   if empty(s:handlers) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   for s:handler in values(s:handlers) | ||||
|     call s:handler(a:job, a:exit_status, a:data) | ||||
|   endfor | ||||
| endfunction | ||||
|  | ||||
| " on_stdout is the stdout handler for jobstart(). It collects the output of | ||||
| " stderr and stores them to the jobs internal stdout list. | ||||
| function! s:on_stdout(job_id, data, event) dict abort | ||||
|   call extend(self.stdout, a:data) | ||||
| endfunction | ||||
|  | ||||
| " on_stderr is the stderr handler for jobstart(). It collects the output of | ||||
| " stderr and stores them to the jobs internal stderr list. | ||||
| function! s:on_stderr(job_id, data, event) dict abort | ||||
|   call extend(self.stderr, a:data) | ||||
| endfunction | ||||
|  | ||||
| " vim: sw=2 ts=2 et | ||||
| @ -34,28 +34,21 @@ function! go#test#Test(bang, compile, ...) abort | ||||
|     endif | ||||
|   endif | ||||
|  | ||||
|   if go#util#has_job() | ||||
|   if go#util#has_job() || has('nvim') | ||||
|     " use vim's job functionality to call it asynchronously | ||||
|     let job_args = { | ||||
|           \ 'cmd': ['go'] + args, | ||||
|     let job_options  = { | ||||
|           \ 'bang': a:bang, | ||||
|           \ 'winid': win_getid(winnr()), | ||||
|           \ 'dir': getcwd(), | ||||
|           \ 'compile_test': a:compile, | ||||
|           \ 'jobdir': fnameescape(expand("%:p:h")), | ||||
|           \ 'for': 'GoTest', | ||||
|           \ 'statustype': 'test', | ||||
|           \ 'errorformat': s:errorformat(), | ||||
|           \ } | ||||
|  | ||||
|     call s:test_job(job_args) | ||||
|     return | ||||
|   elseif has('nvim') | ||||
|     " use nvims's job functionality | ||||
|     if go#config#TermEnabled() | ||||
|       let id = go#term#new(a:bang, ["go"] + args) | ||||
|     else | ||||
|       let id = go#jobcontrol#Spawn(a:bang, "test", "GoTest", args) | ||||
|     if a:compile | ||||
|       let job_options.statustype = 'compile ' . job_options.statustype | ||||
|     endif | ||||
|  | ||||
|     return id | ||||
|     call s:test_job(['go'] + args, job_options) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   call go#cmd#autowrite() | ||||
| @ -129,156 +122,13 @@ function! go#test#Func(bang, ...) abort | ||||
|   call call('go#test#Test', args) | ||||
| endfunction | ||||
|  | ||||
| function! s:test_job(args) abort | ||||
|   let status = { | ||||
|         \ 'desc': 'current status', | ||||
|         \ 'type': "test", | ||||
|         \ 'state': "started", | ||||
|         \ } | ||||
|  | ||||
|   if a:args.compile_test | ||||
|     let status.state = "compiling" | ||||
|   endif | ||||
|  | ||||
| function! s:test_job(cmd, args) abort | ||||
|   " autowrite is not enabled for jobs | ||||
|   call go#cmd#autowrite() | ||||
|  | ||||
|   let state = { | ||||
|         \ 'exited': 0, | ||||
|         \ 'closed': 0, | ||||
|         \ 'exitval': 0, | ||||
|         \ 'messages': [], | ||||
|         \ 'args': a:args, | ||||
|         \ 'compile_test': a:args.compile_test, | ||||
|         \ 'status_dir': expand('%:p:h'), | ||||
|         \ 'started_at': reltime() | ||||
|       \ } | ||||
|  | ||||
|   call go#statusline#Update(state.status_dir, status) | ||||
|  | ||||
|   function! s:callback(chan, msg) dict | ||||
|     call add(self.messages, a:msg) | ||||
|   endfunction | ||||
|  | ||||
|   function! s:exit_cb(job, exitval) dict | ||||
|     let self.exited = 1 | ||||
|     let self.exitval = a:exitval | ||||
|  | ||||
|     let status = { | ||||
|           \ 'desc': 'last status', | ||||
|           \ 'type': "test", | ||||
|           \ 'state': "pass", | ||||
|           \ } | ||||
|  | ||||
|     if self.compile_test | ||||
|       let status.state = "success" | ||||
|     endif | ||||
|  | ||||
|     if a:exitval | ||||
|       let status.state = "failed" | ||||
|     endif | ||||
|  | ||||
|     if go#config#EchoCommandInfo() | ||||
|       if a:exitval == 0 | ||||
|         if self.compile_test | ||||
|           call go#util#EchoSuccess("[test] SUCCESS") | ||||
|         else | ||||
|           call go#util#EchoSuccess("[test] PASS") | ||||
|         endif | ||||
|       else | ||||
|         call go#util#EchoError("[test] FAIL") | ||||
|       endif | ||||
|     endif | ||||
|  | ||||
|     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(self.status_dir, status) | ||||
|  | ||||
|     if self.closed | ||||
|       call s:show_errors(self.args, self.exitval, self.messages) | ||||
|     endif | ||||
|   endfunction | ||||
|  | ||||
|   function! s:close_cb(ch) dict | ||||
|     let self.closed = 1 | ||||
|  | ||||
|     if self.exited | ||||
|       call s:show_errors(self.args, self.exitval, self.messages) | ||||
|     endif | ||||
|   endfunction | ||||
|  | ||||
|   " explicitly bind the callbacks to state so that self within them always | ||||
|   " refers to state. See :help Partial for more information. | ||||
|   let start_options = { | ||||
|         \ 'callback': funcref("s:callback", [], state), | ||||
|         \ 'exit_cb': funcref("s:exit_cb", [], state), | ||||
|         \ 'close_cb': funcref("s:close_cb", [], state) | ||||
|       \ } | ||||
|  | ||||
|   " pre start | ||||
|   let dir = getcwd() | ||||
|   let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|   let jobdir = fnameescape(expand("%:p:h")) | ||||
|   execute cd . jobdir | ||||
|  | ||||
|   call job_start(a:args.cmd, start_options) | ||||
|  | ||||
|   " post start | ||||
|   execute cd . fnameescape(dir) | ||||
|   call go#job#Spawn(a:cmd, a:args) | ||||
| endfunction | ||||
|  | ||||
| " show_errors parses the given list of lines of a 'go test' output and returns | ||||
| " a quickfix compatible list of errors. It's intended to be used only for go | ||||
| " test output. | ||||
| function! s:show_errors(args, exit_val, messages) abort | ||||
|   let l:winid = win_getid(winnr()) | ||||
|  | ||||
|   call win_gotoid(a:args.winid) | ||||
|  | ||||
|   let l:listtype = go#list#Type("GoTest") | ||||
|   if a:exit_val == 0 | ||||
|     call go#list#Clean(l:listtype) | ||||
|     call win_gotoid(l:winid) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   " TODO(bc): When messages is JSON, the JSON should be run through a | ||||
|   " filter to produce lines that are more easily described by errorformat. | ||||
|  | ||||
|   let l:listtype = go#list#Type("GoTest") | ||||
|  | ||||
|   let cd = exists('*haslocaldir') && haslocaldir() ? 'lcd ' : 'cd ' | ||||
|   try | ||||
|     execute cd a:args.jobdir | ||||
|     call go#list#ParseFormat(l:listtype, s:errorformat(), a:messages, join(a:args.cmd)) | ||||
|     let errors = go#list#Get(l:listtype) | ||||
|   finally | ||||
|     execute cd . fnameescape(a:args.dir) | ||||
|   endtry | ||||
|  | ||||
|   if !len(errors) | ||||
|     " failed to parse errors, output the original content | ||||
|     call go#util#EchoError(a:messages) | ||||
|     call go#util#EchoError(a:args.dir) | ||||
|     call win_gotoid(l:winid) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   if a:args.winid != l:winid | ||||
|     call win_gotoid(l:winid) | ||||
|     return | ||||
|   endif | ||||
|  | ||||
|   call go#list#Window(l:listtype, len(errors)) | ||||
|   if !empty(errors) && !a:args.bang | ||||
|     call go#list#JumpToFirst(l:listtype) | ||||
|   endif | ||||
| endfunction | ||||
|  | ||||
|  | ||||
| let s:efm = "" | ||||
| let s:go_test_show_name = 0 | ||||
|  | ||||
|  | ||||
| @ -89,15 +89,6 @@ func! Test_GoTestTestCompilerError() abort | ||||
| endfunc | ||||
|  | ||||
| func! s:test(file, expected, ...) abort | ||||
|   if has('nvim') | ||||
|     " nvim mostly shows test errors correctly, but the the expected errors are | ||||
|     " slightly different; buffer numbers are not the same and stderr doesn't | ||||
|     " seem to be redirected to the job, so the lines from the panic aren't in | ||||
|     " the output to be parsed, and hence are not in the quickfix lists. Once | ||||
|     " those two issues are resolved, this early return should be removed so | ||||
|     " the tests will run for Neovim, too. | ||||
|     return | ||||
|   endif | ||||
|   let $GOPATH = fnameescape(fnamemodify(getcwd(), ':p')) . 'test-fixtures/test' | ||||
|   silent exe 'e ' . $GOPATH . '/src/' . a:file | ||||
|  | ||||
|  | ||||
| @ -167,13 +167,16 @@ function! go#util#Exec(cmd, ...) abort | ||||
|  | ||||
|   let l:bin = a:cmd[0] | ||||
|  | ||||
|   " Lookup the full path, respecting settings such as 'go_bin_path'. On errors, | ||||
|   " CheckBinPath will show a warning for us. | ||||
|   let l:bin = go#path#CheckBinPath(l:bin) | ||||
|   if empty(l:bin) | ||||
|     return ['', 1] | ||||
|   endif | ||||
|  | ||||
|   return call('s:exec', [a:cmd] + a:000) | ||||
|   " Finally execute the command using the full, resolved path. Do not pass the | ||||
|   " unmodified command as the correct program might not exist in $PATH. | ||||
|   return call('s:exec', [[l:bin] + a:cmd[1:]] + a:000) | ||||
| endfunction | ||||
|  | ||||
| function! s:exec(cmd, ...) abort | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Amir Salihefendic
					Amir Salihefendic