Skip to content

Commit d1937dc

Browse files
authored
Merge pull request vim-jp#838 from mikoto2000/add-async-process
2 parents e3023de + 4937a83 commit d1937dc

File tree

3 files changed

+398
-0
lines changed

3 files changed

+398
-0
lines changed
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
let s:save_cpoptions = &cpoptions
2+
set cpoptions&vim
3+
4+
let s:is_windows = has('win32') " This means any versions of windows https://github.com/vim-jp/vital.vim/wiki/Coding-Rule#how-to-check-if-the-runtime-os-is-windows
5+
let s:is_nvim = has('nvim')
6+
7+
let s:TYPE_DICT = type({})
8+
let s:TYPE_LIST = type([])
9+
let s:TYPE_STRING = type('')
10+
11+
function! s:_vital_loaded(V) abort
12+
let s:V = a:V
13+
let s:Process = a:V.import('Process')
14+
endfunction
15+
16+
function! s:_vital_depends() abort
17+
return ['Process']
18+
endfunction
19+
20+
function! s:_ensure_buffer_string(string) abort
21+
if s:is_windows
22+
return s:Process.iconv(a:string, 'char', &encoding)
23+
else
24+
return a:string
25+
endif
26+
endfunction
27+
28+
if !s:is_nvim
29+
" inner callbacks for Vim
30+
function! s:_inner_out_cb(user_out_cb, ch, msg) abort
31+
call a:user_out_cb(s:_ensure_buffer_string(a:msg))
32+
endfunction
33+
34+
function! s:_inner_exit_cb(user_exit_cb, job, exit_code) abort
35+
call a:user_exit_cb(a:exit_code)
36+
endfunction
37+
38+
function! s:_inner_err_cb(user_err_cb, ch, msg) abort
39+
call a:user_err_cb(s:_ensure_buffer_string(result))
40+
endfunction
41+
else
42+
" inner callbacks for Neovim
43+
function! s:_inner_out_cb(user_out_cb, job_id, data, event) abort
44+
for line in a:data
45+
if line !=# ''
46+
call a:user_out_cb(line)
47+
endif
48+
endfor
49+
endfunction
50+
51+
function! s:_inner_exit_cb(user_exit_cb, job_id, exit_code, event) abort
52+
call a:user_exit_cb(a:exit_code)
53+
endfunction
54+
55+
function! s:_inner_err_cb(user_err_cb, job_id, data, event) abort
56+
for line in a:data
57+
if line !=# ''
58+
call a:user_err_cb(line)
59+
endif
60+
endfor
61+
endfunction
62+
endif
63+
64+
" execute({command}, {options})
65+
" {command} = string
66+
" {options} = {
67+
" out_cb: function(stdout_msg), " call per line
68+
" err_cb: function(stderr_msg), " call per line
69+
" exit_cb: function(exit_code), " call on exit
70+
" timeout: Number(ms),
71+
" }
72+
function! s:execute(command, options) abort
73+
if !type(a:options) is s:TYPE_DICT
74+
throw 'vital: AsyncProcess: invalid argument (value type:' . type(a:options) . ')'
75+
endif
76+
77+
" Process a:command argument.
78+
if type(a:command) is s:TYPE_STRING
79+
let command = a:command
80+
elseif type(a:command) is s:TYPE_LIST
81+
let command = join(a:command, ' ')
82+
else
83+
throw 'vital: AsyncProcess: invalid argument (value type:' . type(a:command) . ')'
84+
endif
85+
86+
" build args
87+
let args = []
88+
if stridx(&shell, 'cmd.exe') != -1
89+
" cmd.exe
90+
let args = args + ['/c']
91+
else
92+
" sh, bash, pwsh, etc.
93+
let args = args + ['-c']
94+
endif
95+
let args = args + [command]
96+
97+
let job_id = -1
98+
if s:is_nvim
99+
let options = {}
100+
if has_key(a:options, 'out_cb')
101+
let options['on_stdout'] = function('s:_inner_out_cb', [a:options.out_cb])
102+
endif
103+
if has_key(a:options, 'err_cb')
104+
let options['on_stderr'] = function('s:_inner_err_cb', [a:options.err_cb])
105+
endif
106+
if has_key(a:options, 'exit_cb')
107+
let options['on_exit'] = function('s:_inner_exit_cb', [a:options.exit_cb])
108+
endif
109+
110+
let job_id = jobstart([&shell] + args, options)
111+
112+
if has_key(a:options, 'timeout')
113+
if a:options.timeout > 0
114+
call timer_start(a:options.timeout, {-> jobstop(job_id)})
115+
endif
116+
endif
117+
118+
return {
119+
\ 'stop': function('jobstop', [job_id]),
120+
\ }
121+
else
122+
let options = {}
123+
if has_key(a:options, 'out_cb')
124+
let options['out_cb'] = function('s:_inner_out_cb', [a:options.out_cb])
125+
endif
126+
if has_key(a:options, 'err_cb')
127+
let options['err_cb'] = function('s:_inner_err_cb', [a:options.err_cb])
128+
endif
129+
if has_key(a:options, 'exit_cb')
130+
let options['exit_cb'] = function('s:_inner_exit_cb', [a:options.exit_cb])
131+
endif
132+
133+
let job = job_start([&shell] + args, options)
134+
135+
if has_key(a:options, 'timeout')
136+
if a:options.timeout > 0
137+
call timer_start(a:options.timeout, {-> job_stop(job)})
138+
endif
139+
endif
140+
141+
return {
142+
\ 'stop': function('job_stop', [job]),
143+
\ }
144+
}
145+
146+
endif
147+
endfunction
148+
149+
let &cpoptions = s:save_cpoptions
150+
unlet s:save_cpoptions
151+

doc/vital/System/AsyncProcess.txt

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
*vital/System/AsyncProcess.txt* Cross platform async process executor
2+
3+
Maintainer: mikoto2000 <mikoto2000@gmail.com>
4+
5+
=============================================================================
6+
CONTENTS *Vital.System.AsyncProcess-contents*
7+
8+
Introduction |Vital.System.AsyncProcess-introduction|
9+
Usage |Vital.System.AsyncProcess-usage|
10+
Functions |Vital.System.AsyncProcess-functions|
11+
12+
13+
=============================================================================
14+
INTRODUCTION *Vital.System.AsyncProcess-introduction*
15+
16+
|Vital.System.AsyncProcess| provides a cross-platform functions to execute
17+
async process.
18+
19+
20+
=============================================================================
21+
USAGE *Vital.System.AsyncProcess-usage*
22+
>
23+
" call on exit callback
24+
function! s:my_exit_cb(exit_code) abort
25+
echomsg 'exit_cb status: ' .. a:exit_code
26+
endfunction
27+
28+
" call on out callback(called per line)
29+
function! s:my_out_cb(out_msg) abort
30+
echomsg 'out_cb_bytes: ' .. a:out_msg
31+
endfunction
32+
33+
" call on error callback(called per line)
34+
function! s:my_err_cb(err_msg) abort
35+
echomsg 'err_cb msg: ' .. a:err_msg
36+
endfunction
37+
38+
" execute async process
39+
call s:AsyncProcess.execute(
40+
\ 'ls /',
41+
\ {
42+
\ 'exit_cb': function('s:my_exit_cb'),
43+
\ 'out_cb': function('s:my_out_cb'),
44+
\ 'err_cb': function('s:my_err_cb'),
45+
\ 'timeout': 5000,
46+
\ }
47+
\)
48+
<
49+
50+
=============================================================================
51+
FUNCTIONS *Vital.System.AsyncProcess-functions*
52+
53+
*Vital.System.AsyncProcess.execute()*
54+
execute({args}[, {options}])
55+
Executes a async process specified by {args} (|List| or |String|).
56+
This method need to return a {result} |Dictionary|.
57+
58+
The following attributes are supported in {options}
59+
60+
"exit_cb"
61+
A |function| of callback function called when the process exits.
62+
The callback function has one argument, exit_code(|Number|).
63+
64+
"out_cb"
65+
A |function| of callback function called when the process outputs
66+
some data. The callback function has one argument,
67+
out_msg(|String|).
68+
69+
"err_cb"
70+
A |function| of callback function called when the process outputs
71+
some error data. The callback function has one argument,
72+
err_msg(|String|).
73+
74+
"timeout"
75+
A |Number| of timeout value in milliseconds. If the
76+
process does not exit within the timeout period, it will
77+
be killed.
78+
79+
The following attributes exist in {result} dictionary
80+
81+
"stop()"
82+
A |function| of function to stop the process.
83+
When this function is called, the process will be killed.
84+
85+
=============================================================================
86+
vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl
87+

0 commit comments

Comments
 (0)