"============================================================================= " FILE: vimproc.vim " AUTHOR: Shougo Matsushita (Modified) " Yukihiro Nakadaira (Original) " License: MIT license {{{ " Permission is hereby granted, free of charge, to any person obtaining " a copy of this software and associated documentation files (the " "Software"), to deal in the Software without restriction, including " without limitation the rights to use, copy, modify, merge, publish, " distribute, sublicense, and/or sell copies of the Software, and to " permit persons to whom the Software is furnished to do so, subject to " the following conditions: " " The above copyright notice and this permission notice shall be included " in all copies or substantial portions of the Software. " " THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS " OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF " MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. " IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY " CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, " TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE " SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. " }}} "============================================================================= if exists('g:vimproc#disable') finish endif " Saving 'cpoptions' {{{ let s:save_cpo = &cpo set cpo&vim " }}} function! s:print_error(string) abort echohl Error | echomsg '[vimproc] ' . a:string | echohl None endfunction " Check 'encoding' "{{{ if &encoding =~# '^euc-jp' call s:print_error('Sorry, vimproc does not support this encoding environment.') call s:print_error('You should set ''encoding'' option to "utf-8" ' \ .'and set ''termencoding'' option to "euc-jp".') finish endif "}}} " Version info "{{{ let s:MAJOR_VERSION = 9 let s:MINOR_VERSION = 3 let s:VERSION_NUMBER = str2nr(printf('%2d%02d', s:MAJOR_VERSION, s:MINOR_VERSION)) let s:VERSION_STRING = printf('%d.%d', s:MAJOR_VERSION, s:MINOR_VERSION) "}}} " Global options definition. "{{{ " Set the default of g:vimproc_dll_path by judging OS "{{{ if vimproc#util#is_windows() let s:vimproc_dll_basename = has('win64') ? \ 'vimproc_win64.dll' : 'vimproc_win32.dll' elseif vimproc#util#is_cygwin() let s:vimproc_dll_basename = 'vimproc_cygwin.dll' if system('uname -m') =~? '^x86_64$' let s:vimproc_dll_basename = 'vimproc_cygwin64.dll' endif elseif vimproc#util#is_mac() let s:vimproc_dll_basename = 'vimproc_mac.so' elseif glob('/lib*/ld-linux*64.so.2',1) != '' let s:vimproc_dll_basename = 'vimproc_linux64.so' elseif glob('/lib*/ld-linux*.so.2',1) != '' let s:vimproc_dll_basename = 'vimproc_linux32.so' elseif system('uname -s') =~? '^.\+BSD\n$' let s:vimproc_dll_basename = system( \ 'uname -sm | tr "[:upper:]" "[:lower:]"' \ .' | sed -e "s/ /_/" | xargs -I "{}" echo "vimproc_{}.so"')[0 : -2] else let s:vimproc_dll_basename = 'vimproc_unix.so' endif "}}} call vimproc#util#set_default( \ 'g:vimproc#dll_path', \ expand(':p:h:h') . '/lib/' . s:vimproc_dll_basename, \ 'g:vimproc_dll_path') unlet s:vimproc_dll_basename call vimproc#util#set_default( \'g:vimproc#download_windows_dll', 0) call vimproc#util#set_default( \ 'g:vimproc#password_pattern', \ '\%(Enter \|Repeat \|[Oo]ld \|[Nn]ew \|login ' . \'\|Kerberos \|EncFS \|CVS \|UNIX \| SMB \|LDAP \|\[sudo] ' . \'\|^\|\n\|''s \)\%([Pp]assword\|[Pp]assphrase\)\>', \ 'g:vimproc_password_pattern') call vimproc#util#set_default( \ 'g:vimproc#popen2_commands', { \ 'sh' : 1, 'bash' : 1, 'zsh' : 1, 'csh' : 1, 'tcsh' : 1, \ 'tmux' : 1, 'screen' : 1, 'su' : 1, \ 'python' : 1, 'rhino' : 1, 'ipython' : 1, 'ipython3' : 1, 'yaourt' : 1, \ }, 'g:vimproc_popen2_commands') call vimproc#util#set_default( \ 'g:stdinencoding', 'char') call vimproc#util#set_default( \ 'g:stdoutencoding', 'char') call vimproc#util#set_default( \ 'g:stderrencoding', 'char') "}}} " Constants {{{ function! s:define_signals() abort let s:signames = {} let xs = s:libcall('vp_get_signals', []) for x in xs let [name, val] = split(x, ':') let nr = str2nr(val) let g:vimproc#{name} = nr let s:signames[nr] = name endfor endfunction " }}} let g:vimproc#dll_path = \ vimproc#util#iconv( \ vimproc#util#expand(g:vimproc#dll_path), \ &encoding, vimproc#util#termencoding()) " Backward compatibility. let g:vimproc_password_pattern = g:vimproc#password_pattern if g:vimproc#download_windows_dll && !filereadable(g:vimproc#dll_path) \ && vimproc#util#is_windows() call vimproc#util#try_download_windows_dll(s:VERSION_STRING) endif if !filereadable(g:vimproc#dll_path) || !has('libcall') "{{{ function! vimproc#get_last_status() abort return v:shell_error endfunction function! vimproc#get_last_errmsg() abort return '' endfunction function! vimproc#system(...) abort return call('system', a:000) endfunction if !filereadable(g:vimproc#dll_path) call s:print_error(printf('vimproc''s DLL: "%s" is not found. \ Please read :help vimproc and make it.', g:vimproc#dll_path)) else call s:print_error('libcall feature is disabled in this Vim. \ To use vimproc, you must enable libcall feature.') endif finish endif"}}} function! vimproc#version() abort "{{{ return s:VERSION_NUMBER endfunction"}}} function! vimproc#dll_version() abort "{{{ let [dll_version] = s:libcall('vp_dlversion', []) return str2nr(dll_version) endfunction"}}} "----------------------------------------------------------- " API function! vimproc#open(filename) abort "{{{ let filename = vimproc#util#iconv(fnamemodify(a:filename, ':p'), \ &encoding, vimproc#util#systemencoding()) if filename =~ '^\%(https\?\|ftp\)://' \ && !vimproc#host_exists(filename) " URI is invalid. call s:print_error('vimproc#open: URI "' . filename . '" is invalid.') return endif " Detect desktop environment. if vimproc#util#is_windows() " For URI only. "execute '!start rundll32 url.dll,FileProtocolHandler' filename call s:libcall('vp_open', [filename]) elseif has('win32unix') " Cygwin. call vimproc#system(['cygstart', filename]) elseif executable('xdg-open') " Linux. call vimproc#system_bg(['xdg-open', filename]) elseif exists('$KDE_FULL_SESSION') && $KDE_FULL_SESSION ==# 'true' " KDE. call vimproc#system_bg(['kioclient', 'exec', filename]) elseif exists('$GNOME_DESKTOP_SESSION_ID') " GNOME. call vimproc#system_bg(['gnome-open', filename]) elseif executable('exo-open') " Xfce. call vimproc#system_bg(['exo-open', filename]) elseif vimproc#util#is_mac() && executable('open') " Mac OS. call vimproc#system_bg(['open', filename]) else " Give up. call s:print_error('vimproc#open: Not supported.') endif endfunction"}}} function! vimproc#get_command_name(command, ...) abort "{{{ let path = get(a:000, 0, $PATH) let cnt = a:0 < 2 ? 1 : a:2 let files = split(substitute(vimproc#util#substitute_path_separator( \ vimproc#filepath#which(a:command, path, cnt)), '//', '/', 'g'), '\n') if cnt < 0 return files endif let file = get(files, cnt-1, '') if file == '' throw printf( \ 'vimproc#get_command_name: File "%s" is not found.', a:command) endif return file endfunction"}}} function! s:system(cmdline, is_passwd, input, timeout, is_pty) abort "{{{ let s:last_status = 0 let s:last_errmsg = '' if empty(a:cmdline) return '' endif " Open pipe. try let subproc = (type(a:cmdline[0]) == type('')) ? vimproc#popen3(a:cmdline) : \ a:is_pty ? vimproc#ptyopen(a:cmdline): \ vimproc#pgroup_open(a:cmdline) catch call s:print_error(v:exception) let s:last_status = 1 let s:last_errmsg = v:exception return '' endtry let outbuf = [] let errbuf = [] try if a:input != '' " Write input. call subproc.stdin.write(a:input) endif if a:timeout > 0 && has('reltime') && v:version >= 702 let start = reltime() let deadline = a:timeout let timeout = a:timeout / 2 else let start = 0 let deadline = 0 let timeout = s:read_timeout endif if !a:is_passwd call subproc.stdin.close() endif while !subproc.stdout.eof || !subproc.stderr.eof if deadline "{{{ " Check timeout. let tick = reltimestr(reltime(start)) let elapse = str2nr(tick[:-8] . tick[-6:-4], 10) if deadline <= elapse && !subproc.stdout.eof " Kill process. throw 'vimproc: vimproc#system(): Timeout.' endif let timeout = (deadline - elapse) / 2 endif"}}} if !subproc.stdout.eof "{{{ let out = subproc.stdout.read(-1, timeout) if a:is_passwd && out =~# g:vimproc_password_pattern redraw echo out " Password input. set imsearch=0 let in = vimproc#util#iconv(inputsecret('Input Secret : ')."\", \ &encoding, vimproc#util#termencoding()) call subproc.stdin.write(in) else let outbuf += [out] endif endif"}}} if !subproc.stderr.eof "{{{ let out = subproc.stderr.read(-1, timeout) if a:is_passwd && out =~# g:vimproc_password_pattern redraw echo out " Password input. set imsearch=0 let in = vimproc#util#iconv(inputsecret('Input Secret : ') . "\", \ &encoding, vimproc#util#termencoding()) call subproc.stdin.write(in) else let outbuf += [out] let errbuf += [out] endif endif"}}} endwhile catch call subproc.kill(g:vimproc#SIGTERM) if v:exception !~ '^Vim:Interrupt' call s:print_error(v:throwpoint) call s:print_error(v:exception) endif finally let output = join(outbuf, '') let s:last_errmsg = join(errbuf, '') call subproc.waitpid() endtry " Newline convert. if vimproc#util#is_mac() let output = substitute(output, '\r\n\@!', '\n', 'g') elseif has('win32') || has('win64') let output = substitute(output, '\r\n', '\n', 'g') endif return output endfunction"}}} function! vimproc#system(cmdline, ...) abort "{{{ if type(a:cmdline) == type('') if a:cmdline =~ '&\s*$' let cmdline = substitute(a:cmdline, '&\s*$', '', '') return vimproc#system_bg(cmdline) endif let args = vimproc#parser#parse_statements(a:cmdline) for arg in args let arg.statement = vimproc#parser#parse_pipe(arg.statement) endfor else let args = [{ \ 'statement' : [ { \ 'fd' : { 'stdin' : '', 'stdout' : '', 'stderr' : '' }, \ 'args' : a:cmdline \ }], \ 'condition' : 'always', \ 'cwd' : getcwd(), \ }] endif let timeout = get(a:000, 1, 0) let input = get(a:000, 0, '') return s:system(args, 0, input, timeout, 0) endfunction"}}} function! vimproc#system2(...) abort "{{{ if empty(a:000) return '' endif if len(a:0) > 1 let args = deepcopy(a:000) let args[1] = vimproc#util#iconv( \ args[1], &encoding, vimproc#util#stdinencoding()) else let args = a:000 endif let output = call('vimproc#system', args) " This function converts application encoding to &encoding. let output = vimproc#util#iconv( \ output, vimproc#util#stdoutencoding(), &encoding) let s:last_errmsg = vimproc#util#iconv( \ s:last_errmsg, vimproc#util#stderrencoding(), &encoding) return output endfunction"}}} function! vimproc#system_passwd(cmdline, ...) abort "{{{ if type(a:cmdline) == type('') let args = vimproc#parser#parse_pipe(a:cmdline) else let args = [{ 'fd' : { 'stdin' : '', 'stdout' : '', 'stderr' : '' }, \ 'args' : a:cmdline }] endif let timeout = a:0 >= 2 ? a:2 : 0 let input = a:0 >= 1 ? a:1 : '' let lang_save = $LANG try let $LANG = 'C' return s:system(args, 1, input, timeout, 1) finally let $LANG = lang_save endtry endfunction"}}} function! vimproc#system_bg(cmdline) abort "{{{ " Open pipe. if type(a:cmdline) == type('') if a:cmdline =~ '&\s*$' let cmdline = substitute(a:cmdline, '&\s*$', '', '') return vimproc#system_bg(cmdline) endif let args = vimproc#parser#parse_statements(a:cmdline) for arg in args let arg.statement = vimproc#parser#parse_pipe(arg.statement) endfor else let args = [{ \ 'statement' : [ { \ 'fd' : { 'stdin' : '', 'stdout' : '', 'stderr' : '' }, \ 'args' : a:cmdline \ }], \ 'condition' : 'always', \ 'cwd' : getcwd(), \ }] endif let subproc = vimproc#pgroup_open(args) if empty(subproc) " Not supported path error. return '' endif " Close handles. call s:close_all(subproc) let s:bg_processes[subproc.pid] = subproc.pid return '' endfunction"}}} function! vimproc#system_gui(cmdline) abort "{{{ return vimproc#system_bg(a:cmdline) endfunction"}}} function! vimproc#get_last_status() abort "{{{ return s:last_status endfunction"}}} function! vimproc#get_last_errmsg() abort "{{{ return substitute(vimproc#util#iconv(s:last_errmsg, \ vimproc#util#stderrencoding(), &encoding), '\n$', '', '') endfunction"}}} function! vimproc#shellescape(string) abort "{{{ return string(a:string) endfunction"}}} function! vimproc#fopen(path, ...) abort "{{{ let flags = get(a:000, 0, 'r') let mode = get(a:000, 1, 0644) let fd = s:vp_file_open((s:is_null_device(a:path) \ ? s:null_device : a:path), flags, mode) let proc = s:fdopen(fd, 'vp_file_close', 'vp_file_read', 'vp_file_write') return proc endfunction"}}} function! vimproc#popen2(args, ...) abort "{{{ let args = type(a:args) == type('') ? \ vimproc#parser#split_args(a:args) : \ a:args let is_pty = get(a:000, 0, 0) return s:plineopen(2, [{ \ 'args' : args, \ 'fd' : { 'stdin' : '', 'stdout' : '', 'stderr' : '' }, \ }], is_pty) endfunction"}}} function! vimproc#popen3(args, ...) abort "{{{ let args = type(a:args) == type('') ? \ vimproc#parser#split_args(a:args) : \ a:args let is_pty = get(a:000, 0, 0) return s:plineopen(3, [{ \ 'args' : args, \ 'fd' : { 'stdin' : '', 'stdout' : '', 'stderr' : '' }, \ }], is_pty) endfunction"}}} function! vimproc#plineopen2(commands, ...) abort "{{{ let commands = type(a:commands) == type('') ? \ vimproc#parser#parse_pipe(a:commands) : \ a:commands let is_pty = get(a:000, 0, 0) return s:plineopen(2, commands, is_pty) endfunction"}}} function! vimproc#plineopen3(commands, ...) abort "{{{ let commands = type(a:commands) == type('') ? \ vimproc#parser#parse_pipe(a:commands) : \ a:commands let is_pty = get(a:000, 0, 0) return s:plineopen(3, commands, is_pty) endfunction"}}} function! s:plineopen(npipe, commands, is_pty) abort "{{{ let pid_list = [] let stdin_list = [] let stdout_list = [] let stderr_list = [] let npipe = a:npipe " Open input. let hstdin = (empty(a:commands) || a:commands[0].fd.stdin == '')? \ 0 : vimproc#fopen(a:commands[0].fd.stdin, 'r').fd let is_pty = !vimproc#util#is_windows() && a:is_pty let cnt = 0 for command in a:commands if is_pty && command.fd.stdout == '' && cnt == 0 \ && len(a:commands) != 1 " pty_open() use pipe. let hstdout = 1 else if command.fd.stdout =~ '^>' let mode = 'a' let command.fd.stdout = command.fd.stdout[1:] else let mode = 'w' endif let hstdout = s:is_pseudo_device(command.fd.stdout) ? \ 0 : vimproc#fopen(command.fd.stdout, mode).fd endif if is_pty && command.fd.stderr == '' && cnt == 0 \ && len(a:commands) != 1 " pty_open() use pipe. let hstderr = 1 else if command.fd.stderr =~ '^>' let mode = 'a' let command.fd.stderr = command.fd.stderr[1:] else let mode = 'w' endif let hstderr = s:is_pseudo_device(command.fd.stderr) ? \ 0 : vimproc#fopen(command.fd.stderr, mode).fd endif if command.fd.stderr ==# '/dev/stdout' let npipe = 2 endif let args = s:convert_args(command.args) let command_name = fnamemodify(args[0], ':t:r') let pty_npipe = cnt == 0 \ && hstdin == 0 && hstdout == 0 && hstderr == 0 \ && exists('g:vimproc#popen2_commands') \ && get(g:vimproc#popen2_commands, command_name, 0) != 0 ? \ 2 : npipe if is_pty && (cnt == 0 || cnt == len(a:commands)-1) " Use pty_open(). let pipe = s:vp_pty_open(pty_npipe, \ s:get_winwidth(), winheight(0), \ hstdin, hstdout, hstderr, args) else let pipe = s:vp_pipe_open(pty_npipe, \ hstdin, hstdout, hstderr, args) endif if len(pipe) == 4 let [pid, fd_stdin, fd_stdout, fd_stderr] = pipe let stderr = s:fdopen(fd_stderr, \ 'vp_pipe_close', 'vp_pipe_read', 'vp_pipe_write') else let [pid, fd_stdin, fd_stdout] = pipe let stderr = s:closed_fdopen( \ 'vp_pipe_close', 'vp_pipe_read', 'vp_pipe_write') endif call add(pid_list, pid) let stdin = s:fdopen(fd_stdin, \ 'vp_pipe_close', 'vp_pipe_read', 'vp_pipe_write') let stdin.is_pty = is_pty \ && (cnt == 0 || cnt == len(a:commands)-1) \ && hstdin == 0 call add(stdin_list, stdin) let stdout = s:fdopen(fd_stdout, \ 'vp_pipe_close', 'vp_pipe_read', 'vp_pipe_write') let stdout.is_pty = is_pty \ && (cnt == 0 || cnt == len(a:commands)-1) \ && hstdout == 0 call add(stdout_list, stdout) let stderr.is_pty = is_pty \ && (cnt == 0 || cnt == len(a:commands)-1) \ && hstderr == 0 call add(stderr_list, stderr) let hstdin = stdout_list[-1].fd let cnt += 1 endfor let proc = {} let proc.pid_list = pid_list let proc.pid = pid_list[-1] let proc.stdin = s:fdopen_pipes(stdin_list, \ 'vp_pipes_close', 'read_pipes', 'write_pipes') let proc.stdout = s:fdopen_pipes(stdout_list, \ 'vp_pipes_close', 'read_pipes', 'write_pipes') let proc.stderr = s:fdopen_pipes(stderr_list, \ 'vp_pipes_close', 'read_pipes', 'write_pipes') let proc.get_winsize = s:funcref('vp_get_winsize') let proc.set_winsize = s:funcref('vp_set_winsize') let proc.kill = s:funcref('vp_kill') let proc.waitpid = s:funcref('vp_waitpid') let proc.checkpid = s:funcref('vp_checkpid') let proc.is_valid = 1 let proc.is_pty = is_pty if a:is_pty let proc.ttyname = '' let proc.width = winwidth(0) - &l:numberwidth - &l:foldcolumn let proc.height = winheight(0) let proc.get_winsize = s:funcref('vp_get_winsize') let proc.set_winsize = s:funcref('vp_set_winsize') endif return proc endfunction"}}} let s:null_device = vimproc#util#is_windows() ? 'NUL' : '/dev/null' function! s:is_null_device(filename) abort return a:filename ==# '/dev/null' endfunction function! s:is_pseudo_device(filename) abort "{{{ if vimproc#util#is_windows() && ( \ a:filename ==# '/dev/stdin' \ || a:filename ==# '/dev/stdout' \ || a:filename ==# '/dev/stderr') return 1 endif return a:filename == '' \ || a:filename ==# '/dev/clip' \ || a:filename ==# '/dev/quickfix' endfunction"}}} function! vimproc#pgroup_open(statements, ...) abort "{{{ if type(a:statements) == type('') let statements = \ vimproc#parser#parse_statements(a:statements) for statement in statements let statement.statement = \ vimproc#parser#parse_pipe(statement.statement) endfor else let statements = a:statements endif let is_pty = get(a:000, 0, 0) let npipe = get(a:000, 1, 3) return s:pgroup_open(statements, is_pty && !vimproc#util#is_windows(), npipe) endfunction"}}} function! s:pgroup_open(statements, is_pty, npipe) abort "{{{ let proc = {} let cwd = getcwd() try call vimproc#util#cd(a:statements[0].cwd) let proc.current_proc = \ vimproc#plineopen{a:npipe}(a:statements[0].statement, a:is_pty) finally call vimproc#util#cd(cwd) endtry let proc.pid = proc.current_proc.pid let proc.pid_list = proc.current_proc.pid_list let proc.condition = a:statements[0].condition let proc.statements = a:statements[1:] let proc.stdin = s:fdopen_pgroup(proc, proc.current_proc.stdin, \ 'vp_pgroup_close', 'read_pgroup', 'write_pgroup') let proc.stdout = s:fdopen_pgroup(proc, proc.current_proc.stdout, \ 'vp_pgroup_close', 'read_pgroup', 'write_pgroup') let proc.stderr = s:fdopen_pgroup(proc, proc.current_proc.stderr, \ 'vp_pgroup_close', 'read_pgroup', 'write_pgroup') let proc.kill = s:funcref('vp_pgroup_kill') let proc.waitpid = s:funcref('vp_pgroup_waitpid') let proc.is_valid = 1 let proc.is_pty = 0 " echomsg expand('') " echomsg 'open:' string(map(copy(proc.current_proc.stdin.fd), 'v:val.fd')) " echomsg 'open:' string(map(copy(proc.current_proc.stdout.fd), 'v:val.fd')) " echomsg 'open:' string(map(copy(proc.current_proc.stderr.fd), 'v:val.fd')) return proc endfunction"}}} function! vimproc#ptyopen(commands, ...) abort "{{{ let commands = type(a:commands) == type('') ? \ vimproc#parser#parse_pipe(a:commands) : \ a:commands let npipe = get(a:000, 0, 3) return s:plineopen(npipe, commands, !vimproc#util#is_windows()) endfunction"}}} function! vimproc#socket_open(host, port) abort "{{{ if !vimproc#host_exists(a:host) throw printf('vimproc: host "%s" does not exist', a:host) endif let fd = s:vp_socket_open(a:host, a:port) return s:fdopen(fd, 'vp_socket_close', 'vp_socket_read', 'vp_socket_write') endfunction"}}} function! vimproc#host_exists(host) abort "{{{ let rval = s:vp_host_exists( \ substitute(substitute(a:host, '^\a\+://', '', ''), '/.*$', '', '')) return 0 + rval endfunction"}}} function! vimproc#kill(pid, sig) abort "{{{ if a:sig == 0 && vimproc#util#is_windows() " Use waitpid(). let cond = s:waitpid(a:pid, 1)[0] if cond ==# 'error' let s:last_errmsg = 'waitpid error' endif return cond !=# 'run' endif try let [ret] = s:libcall('vp_kill', [a:pid, a:sig]) catch let s:last_errmsg = v:exception return 1 endtry return ret endfunction"}}} function! vimproc#decode_signal(signal) abort "{{{ return get(s:signames, a:signal, 'UNKNOWN') endfunction"}}} function! vimproc#write(filename, string, ...) abort "{{{ if a:string == '' return endif let mode = get(a:000, 0, \ a:filename =~ '^>' ? 'a' : 'w') let filename = a:filename =~ '^>' ? \ a:filename[1:] : a:filename if s:is_null_device(filename) " Nothing. elseif filename ==# '/dev/clip' " Write to clipboard. if mode =~ 'a' let @+ .= a:string else let @+ = a:string endif elseif filename ==# '/dev/quickfix' " Write to quickfix. let qflist = getqflist() for str in split(a:string, '\n\|\r\n') if str =~ '^.\+:.\+:.\+$' let line = split(str[2:], ':') let filename = str[:1] . line[0] if len(line) >= 3 && line[1] =~ '^\d\+$' call add(qflist, { \ 'filename' : filename, \ 'lnum' : line[1], \ 'text' : join(line[2:], ':'), \ }) else call add(qflist, { \ 'text' : str, \ }) endif endif endfor call setqflist(qflist) else " Write file. let hfile = vimproc#fopen(filename, mode) call hfile.write(a:string) call hfile.close() endif endfunction"}}} function! vimproc#readdir(dirname) abort "{{{ let dirname = vimproc#util#expand(a:dirname) if dirname == '' let dirname = getcwd() endif let dirname = substitute(dirname, '.\zs/$', '', '') if !vimproc#util#is_windows() let dirname = substitute(dirname, '//', '/', 'g') endif if !isdirectory(dirname) return [] endif let dirname = vimproc#util#iconv(dirname, &encoding, \ vimproc#util#systemencoding()) try let files = s:libcall('vp_readdir', [dirname]) catch /vp_readdir/ return [] endtry call map(filter(files, 'v:val !~ "/\\.\\.\\?$"'), 'vimproc#util#iconv( \ v:val, vimproc#util#systemencoding(), &encoding)') if vimproc#util#is_windows() call map(files, 'vimproc#util#substitute_path_separator(v:val)') endif call map(files, "substitute(v:val, '/\\./', '/', 'g')") return files endfunction"}}} function! vimproc#delete_trash(filename) abort "{{{ if !vimproc#util#is_windows() call s:print_error('Not implemented in this platform.') return endif let filename = a:filename if !filewritable(filename) && !isdirectory(filename) return 1 endif " Substitute path separator to "/". let filename = substitute( \ fnamemodify(filename, ':p'), '/', '\\', 'g') " Delete last /. if filename =~ '[^:][/\\]$' " Delete last /. let filename = filename[: -2] endif " Encoding conversion. let filename = vimproc#util#iconv(filename, \ &encoding, vimproc#util#systemencoding()) let [ret] = s:libcall('vp_delete_trash', [filename]) return str2nr(ret) endfunction"}}} function! vimproc#test_readdir(dirname) abort "{{{ let start = reltime() call split(glob(a:dirname.'/*'), '\n') echomsg reltimestr(reltime(start)) let start = reltime() call vimproc#readdir(a:dirname) echomsg reltimestr(reltime(start)) endfunction"}}} function! s:close_all(self) abort "{{{ if has_key(a:self, 'stdin') call a:self.stdin.close() endif if has_key(a:self, 'stdout') call a:self.stdout.close() endif if has_key(a:self, 'stderr') call a:self.stderr.close() endif endfunction"}}} function! s:close() dict "{{{ if self.is_valid call self.f_close() endif let self.is_valid = 0 let self.eof = 1 let self.__eof = 1 endfunction"}}} function! s:read(...) dict "{{{ if self.__eof let self.eof = 1 return '' endif let maxsize = get(a:000, 0, -1) let timeout = get(a:000, 1, s:read_timeout) let buf = [] let eof = 0 while maxsize != 0 && !eof let [out, eof] = self.f_read(maxsize, \ (timeout < s:read_timeout ? timeout : s:read_timeout)) if out ==# '' let timeout -= s:read_timeout if timeout <= 0 break endif else let buf += [out] let maxsize -= len(out) let timeout = 0 endif endwhile let self.eof = eof let self.__eof = eof return join(buf, '') endfunction"}}} function! s:read_lines(...) dict "{{{ if self.__eof return [] endif let lines = self.buffer[:-2] let res = get(self.buffer, -1, '') let out = call(self.read, a:000, self) if out !=# '' let outs = split(out, '\r*\n\|\r', 1) let res .= outs[0] if len(outs) > 1 let lines += [substitute(res, '\r*$', '', '')] + outs[1:-2] let res = outs[-1] endif endif if self.__eof || out ==# '' if res !=# '' let lines += [res] endif let self.buffer = [] else let self.buffer = [res] endif return lines endfunction"}}} function! s:read_line(...) dict "{{{ let line = '' if !self.__eof && len(self.buffer) <= 1 let lines = call(self.read_lines, a:000, self) let self.buffer = lines[1:] + self.buffer let line = get(lines, 0, '') elseif !empty(self.buffer) let [line; self.buffer] = self.buffer endif let self.eof = self.__eof && empty(self.buffer) return line endfunction"}}} function! s:write(str, ...) dict "{{{ let timeout = get(a:000, 0, s:write_timeout) return self.f_write(a:str, timeout) endfunction"}}} function! s:fdopen(fd, f_close, f_read, f_write) abort "{{{ return { \ 'fd' : a:fd, \ 'eof' : 0, '__eof' : 0, 'is_valid' : 1, 'buffer' : [], \ 'f_close' : s:funcref(a:f_close), 'f_read' : s:funcref(a:f_read), 'f_write' : s:funcref(a:f_write), \ 'close' : s:funcref('close'), 'read' : s:funcref('read'), 'write' : s:funcref('write'), \ 'read_line' : s:funcref('read_line'), 'read_lines' : s:funcref('read_lines'), \} endfunction"}}} function! s:closed_fdopen(f_close, f_read, f_write) abort "{{{ return { \ 'fd' : -1, \ 'eof' : 1, '__eof' : 1, 'is_valid' : 0, 'buffer' : [], \ 'f_close' : s:funcref(a:f_close), 'f_read' : s:funcref(a:f_read), 'f_write' : s:funcref(a:f_write), \ 'close' : s:funcref('close'), 'read' : s:funcref('read'), 'write' : s:funcref('write'), \ 'read_line' : s:funcref('read_line'), 'read_lines' : s:funcref('read_lines'), \} endfunction"}}} function! s:fdopen_pty(fd_stdin, fd_stdout, f_close, f_read, f_write) abort "{{{ return { \ 'eof' : 0, '__eof' : 0, 'is_valid' : 1, 'buffer' : [], \ 'fd_stdin' : a:fd_stdin, 'fd_stdout' : a:fd_stdout, \ 'f_close' : s:funcref(a:f_close), 'f_read' : s:funcref(a:f_read), 'f_write' : s:funcref(a:f_write), \ 'close' : s:funcref('close'), 'read' : s:funcref('read'), 'write' : s:funcref('write'), \ 'read_line' : s:funcref('read_line'), 'read_lines' : s:funcref('read_lines'), \} endfunction"}}} function! s:fdopen_pipes(fd, f_close, f_read, f_write) abort "{{{ return { \ 'eof' : 0, '__eof' : 0, 'is_valid' : 1, 'buffer' : [], \ 'fd' : a:fd, \ 'f_close' : s:funcref(a:f_close), \ 'close' : s:funcref('close'), 'read' : s:funcref(a:f_read), 'write' : s:funcref(a:f_write), \ 'read_line' : s:funcref('read_line'), 'read_lines' : s:funcref('read_lines'), \} endfunction"}}} function! s:fdopen_pgroup(proc, fd, f_close, f_read, f_write) abort "{{{ return { \ 'eof' : 0, '__eof' : 0, 'is_valid' : 1, 'buffer' : [], \ 'proc' : a:proc, 'fd' : a:fd, \ 'f_close' : s:funcref(a:f_close), \ 'close' : s:funcref('close'), 'read' : s:funcref(a:f_read), 'write' : s:funcref(a:f_write), \ 'read_line' : s:funcref('read_line'), 'read_lines' : s:funcref('read_lines'), \} endfunction"}}} function! s:garbage_collect(is_force) abort "{{{ for pid in values(s:bg_processes) " Check processes. try let [cond, _] = s:libcall('vp_waitpid', [pid]) " echomsg string([pid, cond, _]) if cond !=# 'run' || a:is_force if cond !=# 'exit' " Kill process. call vimproc#kill(pid, g:vimproc#SIGTERM) endif if vimproc#util#is_windows() call s:libcall('vp_close_handle', [pid]) endif call remove(s:bg_processes, pid) endif catch " Ignore error. endtry endfor endfunction"}}} " For debug API. function! vimproc#_get_bg_processes() abort "{{{ return s:bg_processes endfunction"}}} "----------------------------------------------------------- " UTILS function! s:str2hd(str) abort return join(map(range(len(a:str)), \ 'printf("%02X", char2nr(a:str[v:val]))'), '') endfunction function! s:hd2str(hd) abort " a:hd is a list because to avoid copying the value. return get(s:libcall('vp_decode', [a:hd[0]]), 0, '') endfunction function! s:hd2str_lua(hd) abort let ret = [] lua << EOF do local ret = vim.eval('ret') local hd = vim.eval('a:hd[0]') local len = string.len(hd) local s = {} for i = 1, len, 2 do table.insert(s, string.char(tonumber(string.sub(hd, i, i+1), 16))) end ret:add(table.concat(s)) end EOF return ret[0] endfunction function! s:str2list(str) abort return map(range(len(a:str)), 'char2nr(a:str[v:val])') endfunction function! s:list2str(lis) abort return s:hd2str(s:list2hd([a:lis])) endfunction function! s:hd2list(hd) abort return map(split(a:hd, '..\zs'), 'str2nr(v:val, 16)') endfunction function! s:list2hd(lis) abort return join(map(a:lis, 'printf("%02X", v:val)'), '') endfunction function! s:convert_args(args) abort "{{{ if empty(a:args) return [] endif let args = map(copy(a:args), 'vimproc#util#iconv( \ v:val, &encoding, vimproc#util#systemencoding())') if vimproc#util#is_windows() && !executable(a:args[0]) " Search from internal commands. let internal_commands = [ \ 'copy', 'date', 'del', 'dir', 'echo', 'erase', 'for', 'ftype', \ 'if', 'md', 'mkdir', 'move', 'path', 'rd', 'ren', 'rename', \ 'rmdir', 'start', 'time', 'type', 'ver', 'vol'] let index = index(internal_commands, a:args[0], 0, 1) if index >= 0 " Use cmd.exe return ['cmd', '/c', args[0]] + args[1:] endif endif let command_name = vimproc#get_command_name(a:args[0]) return map(vimproc#analyze_shebang(command_name), 'vimproc#util#iconv( \ v:val, &encoding, vimproc#util#systemencoding())') + args[1:] endfunction"}}} function! vimproc#analyze_shebang(filename) abort "{{{ if !filereadable(a:filename) || \ getfsize(a:filename) > 100000 || \ (vimproc#util#is_windows() && \ '.'.fnamemodify(a:filename, ':e') !~? \ '^'.substitute($PATHEXT, ';', '$\\|^', 'g').'$') " Maybe a binary file. return [a:filename] endif let lines = readfile(a:filename, '', 1) if empty(lines) || lines[0] !~ '^#!.\+' " Shebang not found. return [a:filename] endif " Get shebang line. let shebang = split(matchstr(lines[0], '^#!\zs.\+')) " Convert command name. if vimproc#util#is_windows() \ && shebang[0] =~ '^/' let shebang[0] = vimproc#get_command_name( \ fnamemodify(shebang[0], ':t')) endif return shebang + [a:filename] endfunction"}}} "----------------------------------------------------------- " LOW LEVEL API augroup vimproc autocmd VimLeave * call s:finalize() autocmd CursorHold,BufWritePost * call s:garbage_collect(0) augroup END " Initialize. let s:lasterr = [] let s:read_timeout = 100 let s:write_timeout = 100 let s:bg_processes = {} if vimproc#util#has_lua() function! s:split(str, sep) abort let result = [] lua << EOF do local result = vim.eval('result') local str = vim.eval('a:str') local sep = vim.eval('a:sep') local last if string.find(str, sep, 1, true) == nil then result:add(str) else for part, pos in string.gmatch(str, '(.-)' .. sep .. '()') do result:add(part) last = pos end result:add(string.sub(str, last)) end end EOF return result endfunction else function! s:split(str, sep) abort let [result, pos] = [[], 0] while 1 let tmp = stridx(a:str, a:sep, pos) if tmp == -1 call add(result, strpart(a:str, pos)) break endif call add(result, strpart(a:str, pos, tmp - pos)) let pos = tmp + 1 endwhile return result endfunction endif " Encode a 32-bit integer into a 5-byte string. function! s:encode_size(n) abort " Set each bit7 to 1 in order to avoid NUL byte. return printf("%c%c%c%c%c", \ ((a:n / 0x10000000) % 0x80) + 0x80, \ ((a:n / 0x200000) % 0x80) + 0x80, \ ((a:n / 0x4000) % 0x80) + 0x80, \ ((a:n / 0x80) % 0x80) + 0x80, \ ( a:n % 0x80) + 0x80) endfunction " Decode a 32-bit integer from a 5-byte string. function! s:decode_size(str, off) abort return \ (char2nr(a:str[a:off + 0]) - 0x80) * 0x10000000 + \ (char2nr(a:str[a:off + 1]) - 0x80) * 0x200000 + \ (char2nr(a:str[a:off + 2]) - 0x80) * 0x4000 + \ (char2nr(a:str[a:off + 3]) - 0x80) * 0x80 + \ (char2nr(a:str[a:off + 4]) - 0x80) endfunction " Encode a list into a string. function! s:encode_list(arr) abort " End Of Value let EOV = "\xFF" " EOV, encoded size0, data0, EOV, encoded size1, data1, EOV, ... return empty(a:arr) ? '' : \ (EOV . join(map(copy(a:arr), 's:encode_size(strlen(v:val)) . v:val'), EOV) . EOV) endfunction " Decode a list from a string. function! s:decode_list(str) abort let err = 0 " End Of Value let EOV = "\xFF" if a:str[0] != EOV let err = 1 return [[a:str], err] endif let arr = [] let slen = strlen(a:str) let off = 1 while slen - off >= 5 let size = s:decode_size(a:str, off) let arr += [a:str[off + 5 : off + 5 + size - 1]] let off += 5 + size + 1 endwhile return [arr, err] endfunction function! s:libcall(func, args) abort "{{{ let stack_buf = libcall(g:vimproc#dll_path, a:func, s:encode_list(a:args)) if empty(stack_buf) return [] endif let [result, err] = s:decode_list(stack_buf) if err let s:lasterr = result let msg = vimproc#util#iconv(string(result), \ vimproc#util#systemencoding(), &encoding) throw printf('vimproc: %s: %s', a:func, msg) endif return result endfunction"}}} " args[0]: fd, args[1]: count, args[2]: timeout function! s:libcall_raw_read(func, args) abort "{{{ let [err, hd] = s:libcall(a:func, a:args) return [hd, err] endfunction "}}} " args[0]: fd, args[1]: data, args[2]: timeout function! s:libcall_raw_write(func, args) abort "{{{ return s:libcall(a:func, [a:args[0], a:args[2], a:args[1]]) endfunction "}}} function! s:SID_PREFIX() abort if !exists('s:sid_prefix') let s:sid_prefix = matchstr(expand(''), \ '\d\+_\zeSID_PREFIX$') endif return s:sid_prefix endfunction " Get funcref. function! s:funcref(funcname) abort return function(s:SID_PREFIX().a:funcname) endfunction function! s:finalize() abort call s:garbage_collect(1) if exists('s:dll_handle') call s:vp_dlclose(s:dll_handle) endif endfunction function! s:vp_dlopen(path) abort let [handle] = s:libcall('vp_dlopen', [a:path]) return handle endfunction function! s:vp_dlclose(handle) abort call s:libcall('vp_dlclose', [a:handle]) endfunction function! s:vp_file_open(path, flags, mode) abort let [fd] = s:libcall('vp_file_open', [a:path, a:flags, a:mode]) return fd endfunction function! s:vp_file_close() dict if self.fd != 0 call s:libcall('vp_file_close', [self.fd]) let self.fd = 0 endif endfunction function! s:vp_file_read(number, timeout) dict let [hd, eof] = s:libcall_raw_read('vp_file_read', [self.fd, a:number, a:timeout]) return [hd, eof] endfunction function! s:vp_file_write(hd, timeout) dict let [nleft] = s:libcall_raw_write('vp_file_write', [self.fd, a:hd, a:timeout]) return nleft endfunction function! s:quote_arg(arg) abort return (a:arg == '' || a:arg =~ '[ "]') ? \ '"' . substitute(a:arg, '"', '\\"', 'g') . '"' : a:arg endfunction function! s:vp_pipe_open(npipe, hstdin, hstdout, hstderr, argv) abort "{{{ try if vimproc#util#is_windows() let cmdline = s:quote_arg(substitute(a:argv[0], '/', '\', 'g')) for arg in a:argv[1:] let cmdline .= ' ' . s:quote_arg(arg) endfor let [pid; fdlist] = s:libcall('vp_pipe_open', \ [a:npipe, a:hstdin, a:hstdout, a:hstderr, cmdline]) else let [pid; fdlist] = s:libcall('vp_pipe_open', \ [a:npipe, a:hstdin, a:hstdout, a:hstderr, len(a:argv)] + a:argv) endif catch call s:print_error(v:throwpoint) call s:print_error(v:exception) call s:print_error( \ 'Error occurred in calling s:vp_pipe_open()') call s:print_error(printf( \ 'a:argv = %s', string(a:argv))) call s:print_error(printf( \ 'original a:argv = %s', vimproc#util#iconv( \ string(a:argv), vimproc#util#systemencoding(), &encoding))) endtry if a:npipe != len(fdlist) call s:print_error(printf( \ 'a:npipe = %d, a:argv = %s', a:npipe, string(a:argv))) call s:print_error(printf( \ 'pid = %d, fdlist = %s', pid, string(fdlist))) echoerr 'Bug behavior is detected!: ' . pid endif return [pid] + fdlist endfunction"}}} function! s:vp_pipe_close() dict " echomsg 'close:'.self.fd if self.fd != 0 call s:libcall('vp_pipe_close', [self.fd]) let self.fd = 0 endif endfunction function! s:vp_pipes_close() dict for fd in self.fd try call fd.close() catch /vimproc: vp_pipe_close: / " Ignore error. endtry endfor endfunction function! s:vp_pgroup_close() dict call self.fd.close() endfunction function! s:vp_pipe_read(number, timeout) dict if self.fd == 0 return ['', 1] endif let [hd, eof] = s:libcall_raw_read('vp_pipe_read', [self.fd, a:number, a:timeout]) return [hd, eof] endfunction function! s:vp_pipe_write(hd, timeout) dict if self.fd == 0 return 0 endif let [nleft] = s:libcall_raw_write('vp_pipe_write', [self.fd, a:hd, a:timeout]) return nleft endfunction function! s:read_pipes(...) dict "{{{ if type(self.fd[-1]) != type({}) let self.eof = 1 return '' endif let number = get(a:000, 0, -1) let timeout = get(a:000, 1, s:read_timeout) let output = self.fd[-1].read(number, timeout) let self.eof = self.fd[-1].eof return output endfunction"}}} function! s:write_pipes(str, ...) dict "{{{ let timeout = get(a:000, 0, s:write_timeout) if self.fd[0].eof return 0 endif " Write data. let nleft = self.fd[0].write(a:str, timeout) let self.eof = self.fd[0].eof return nleft endfunction"}}} function! s:read_pgroup(...) dict "{{{ let number = get(a:000, 0, -1) let timeout = get(a:000, 1, s:read_timeout) let output = '' if !self.fd.eof let output = self.fd.read(number, timeout) endif if self.proc.current_proc.stdout.eof \ && self.proc.current_proc.stderr.eof " Get status. let [cond, status] = self.proc.current_proc.waitpid() if empty(self.proc.statements) \ || (self.proc.condition ==# 'true' && status) \ || (self.proc.condition ==# 'false' && !status) let self.proc.statements = [] " Caching status. let self.proc.cond = cond let self.proc.status = status if has_key(self.proc.current_proc, 'pipe_status') let self.proc.pipe_status = self.proc.current_proc.pipe_status endif else " Initialize next statement. let cwd = getcwd() try call vimproc#util#cd(self.proc.statements[0].cwd) let proc = vimproc#plineopen3( \ self.proc.statements[0].statement) finally call vimproc#util#cd(cwd) endtry let self.proc.current_proc = proc let self.pid = proc.pid let self.pid_list = proc.pid_list let self.proc.pid = proc.pid let self.proc.pid_list = proc.pid_list let self.proc.condition = self.proc.statements[0].condition let self.proc.statements = self.proc.statements[1:] let self.proc.stdin = s:fdopen_pgroup( \ self.proc, proc.stdin, \ 'vp_pgroup_close', 'read_pgroup', 'write_pgroup') let self.proc.stdout = s:fdopen_pgroup( \ self.proc, proc.stdout, \ 'vp_pgroup_close', 'read_pgroup', 'write_pgroup') let self.proc.stderr = s:fdopen_pgroup( \ self.proc, proc.stderr, \ 'vp_pgroup_close', 'read_pgroup', 'write_pgroup') endif endif if self.proc.current_proc.stdout.eof let self.proc.stdout.eof = 1 let self.proc.stdout.__eof = 1 endif if self.proc.current_proc.stderr.eof let self.proc.stderr.eof = 1 let self.proc.stderr.__eof = 1 endif return output endfunction"}}} function! s:write_pgroup(str, ...) dict "{{{ let timeout = get(a:000, 0, s:write_timeout) let nleft = 0 if !self.fd.eof " Write data. let nleft = self.fd.write(a:str, timeout) endif return nleft endfunction"}}} function! s:vp_pty_open(npipe, width, height, hstdin, hstdout, hstderr, argv) abort let [pid; fdlist] = s:libcall('vp_pty_open', \ [a:npipe, a:width, a:height, \ a:hstdin, a:hstdout, a:hstderr, len(a:argv)] + a:argv) return [pid] + fdlist endfunction function! s:vp_pty_close() dict call s:libcall('vp_pty_close', [self.fd]) endfunction function! s:vp_pty_read(number, timeout) dict let [hd, eof] = s:libcall_raw_read('vp_pty_read', [self.fd, a:number, a:timeout]) return [hd, eof] endfunction function! s:vp_pty_write(hd, timeout) dict let [nleft] = s:libcall_raw_write('vp_pty_write', [self.fd, a:hd, a:timeout]) return nleft endfunction function! s:vp_get_winsize() dict let [width, height] = [s:get_winwidth(), winheight(0)] if !vimproc#util#is_windows() for pid in self.pid_list let [width, height] = s:libcall('vp_pty_get_winsize', [pid]) endfor endif return [width, height] endfunction function! s:vp_set_winsize(width, height) dict if vimproc#util#is_windows() || !self.is_valid \ || (abs(a:width - self.width) < 3 && abs(a:height - self.height) < 3) \ || !self.is_pty return endif let self.width = a:width let self.height = a:height try if self.stdin.eof == 0 && self.stdin.fd[-1].is_pty call s:libcall('vp_pty_set_winsize', \ [self.stdin.fd[-1].fd, a:width-5, a:height]) endif if self.stdout.eof == 0 && self.stdout.fd[0].is_pty call s:libcall('vp_pty_set_winsize', \ [self.stdout.fd[0].fd, a:width-5, a:height]) endif if self.stderr.eof == 0 && self.stderr.fd[0].is_pty call s:libcall('vp_pty_set_winsize', \ [self.stderr.fd[0].fd, a:width-5, a:height]) endif catch return endtry " Send SIGWINCH = 28 signal. for pid in self.pid_list call vimproc#kill(pid, g:vimproc#SIGWINCH) endfor endfunction function! s:vp_kill(...) dict let sig = get(a:000, 0, g:vimproc#SIGTERM) if sig != 0 call s:close_all(self) let self.is_valid = 0 endif let ret = 0 for pid in get(self, 'pid_list', [self.pid]) call s:waitpid(pid, 1) let ret = vimproc#kill(pid, sig) endfor return ret endfunction function! s:vp_pgroup_kill(...) dict let sig = get(a:000, 0, g:vimproc#SIGTERM) if sig != 0 call s:close_all(self) let self.is_valid = 0 endif if self.pid == 0 " Ignore. return endif return self.current_proc.kill(sig) endfunction function! s:waitpid(pid, ...) abort let nohang = a:0 ? a:1 : 0 try while 1 let [cond, status] = s:libcall('vp_waitpid', [a:pid]) " echomsg string([a:pid, cond, status]) if cond !=# 'run' || nohang break endif endwhile if cond ==# 'run' " Add process list. let s:bg_processes[a:pid] = a:pid let [cond, status] = ['exit', '0'] elseif vimproc#util#is_windows() call s:libcall('vp_close_handle', [a:pid]) endif let s:last_status = str2nr(status) catch /No child processes/ let [cond, status] = ['exit', '0'] let s:last_status = 0 catch let [cond, status] = ['error', '0'] let s:last_status = -1 endtry return [cond, str2nr(status)] endfunction function! s:vp_checkpid() dict try let [cond, status] = s:libcall('vp_waitpid', [self.pid]) if cond !=# 'run' let [self.cond, self.status] = [cond, status] endif catch /waitpid() error:\|vp_waitpid:/ let [cond, status] = ['error', '0'] endtry return [cond, str2nr(status)] endfunction function! s:vp_waitpid(...) dict let nohang = a:0 ? a:1 : 0 call s:close_all(self) let self.is_valid = 0 if has_key(self, 'cond') && has_key(self, 'status') " Use cache. let [cond, status] = [self.cond, self.status] else let [cond, status] = s:waitpid(self.pid, nohang) endif if cond ==# 'exit' let self.pid = 0 endif if has_key(self, 'pid_list') if !has_key(self, 'pipe_status') let self.pipe_status = repeat([['run', 0]], len(self.pid_list)) endif let self.pipe_status[:] = map(self.pipe_status[:-2], \ 'v:val[0] !=# "run" ? v:val : s:waitpid(self.pid_list[v:key], nohang)') \ + [[cond, status]] endif return [cond, status] endfunction function! s:vp_pgroup_waitpid() dict call s:close_all(self) let self.is_valid = 0 if !has_key(self, 'cond') || \ !has_key(self, 'status') return s:waitpid(self.pid) endif return [self.cond, self.status] endfunction function! s:vp_socket_open(host, port) abort let [socket] = s:libcall('vp_socket_open', [a:host, a:port]) return socket endfunction function! s:vp_socket_close() dict call s:libcall('vp_socket_close', [self.fd]) let self.is_valid = 0 endfunction function! s:vp_socket_read(number, timeout) dict let [hd, eof] = s:libcall_raw_read('vp_socket_read', \ [self.fd, a:number, a:timeout]) return [hd, eof] endfunction function! s:vp_socket_write(hd, timeout) dict let [nleft] = s:libcall_raw_write('vp_socket_write', \ [self.fd, a:hd, a:timeout]) return nleft endfunction function! s:vp_host_exists(host) abort let [rval] = s:libcall('vp_host_exists', [a:host]) return rval endfunction function! s:get_winwidth() abort return winwidth(0) - &l:numberwidth - &l:foldcolumn endfunction " Initialize. if !exists('s:dll_handle') let s:dll_handle = s:vp_dlopen(g:vimproc#dll_path) let s:last_status = 0 let s:last_errmsg = '' call s:define_signals() endif " vimproc dll version check. "{{{ try if vimproc#dll_version() != vimproc#version() call s:print_error(printf('Your vimproc binary version is "%d",'. \ ' but vimproc version is "%d".', \ vimproc#dll_version(), vimproc#version())) if g:vimproc#download_windows_dll && vimproc#util#is_windows() if vimproc#util#try_update_windows_dll(s:VERSION_STRING) call s:print_error('DLL automatically update succeeded.') call s:print_error('Please restart Vim.') endif endif endif catch call s:print_error(v:throwpoint) call s:print_error(v:exception) call s:print_error('Your vimproc binary is not compatible with this vimproc!') call s:print_error('Please re-compile it.') endtry "}}} " Restore 'cpoptions' {{{ let &cpo = s:save_cpo unlet s:save_cpo " }}} " __END__ " vim:foldmethod=marker:fen:sw=2:sts=2