-
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathJob.txt
260 lines (209 loc) · 9.03 KB
/
Job.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
*Vital/System/Job.txt* A job wrapper for Vim and Neovim
Author: Alisue <[email protected]>
=============================================================================
CONTENTS *Vital.System.Job-contents*
INTRODUCTION |Vital.System.Job-introduction|
USAGE |Vital.System.Job-usage|
CALLBACKS |Vital.System.Job-callbacks|
PROMISE |Vital.System.Job-promise|
FUNCTION |Vital.System.Job-function|
INSTANCE |Vital.System.Job-instance|
=============================================================================
INTRODUCTION *Vital.System.Job-introduction*
*Vital.System.Job* is a module to provide a way to use a job feature of Vim
and Neovim so that developers of Vim plugins do not need to write separate
codes for Vim and Neovim.
Note that prior to Vim 8.0.0027, |Vital.System.Job-instance.wait()| method
enter infinity-loop so this library does not support.
Note that prior to Vim 8.0.0107, the channel handling was different so
it would have performance issues.
=============================================================================
USAGE *Vital.System.Job-usage*
Use |Vitalizer| to inject the module into your plugin like
>
:Vitalize --name=plugin-name . +System.Job
<
Then import the module with |vital#{plugin-name}#import()| like
>
let s:Job = vital#{plugin-name}#import('System.Job')
<
The following code start "git status" and store its' stdout/stderr output in
the instance attribute.
>
function! s:on_stdout(data) abort dict
let self.stdout[-1] .= a:data[0]
call extend(self.stdout, a:data[1:])
endfunction
function! s:on_stderr(data) abort dict
let self.stderr[-1] .= a:data[0]
call extend(self.stderr, a:data[1:])
endfunction
function! s:on_exit(exitval) abort dict
let self.exit_status = a:exitval
endfunction
let job = s:Job.start(['git', 'status'], {
\ 'stdout': [''],
\ 'stderr': [''],
\ 'exit_status': -1,
\ 'on_stdout': function('s:on_stdout'),
\ 'on_stderr': function('s:on_stderr'),
\ 'on_exit': function('s:on_exit'),
\})
<
Note that this module DO NOT change the handling of "data" by operating
system, mean that you may need to normalize "\r" which may appear on each
end of line in Windows. See |Vital.System.Job-callbacks| for detail.
-----------------------------------------------------------------------------
CALLBACKS *Vital.System.Job-callbacks*
"on_stdout" or "on_stderr" callbacks will be called with "data" argument
which contains received data as a newline (\n) separated list.
Note that the data may be incomplete. For example, "Hello\nGood\nBye\n" may
be received as
1. ["Hello", "Good", "Bye", ""]
2. ["Hello", ""], ["Good", ""], and ["Bye", ""]
3. ["Hello", "Go"], ["od", "Bye"], and ["", ""]
To normalize the data, see an example code in |Vital.System.Job-usage|.
Additionally, programs running on Windows may use CRLF (\r\n) instead of
LF (\n) for line delimiter. In this case, developers requires to handle
trailing CR (\r) characters to normalize the result. Like:
>
function! s:on_stdout(data) abort dict
" Remove trailing CRs
call map(a:data, 'v:val[-1:] ==# "\r" ? v:val[:-2] : v:val')
let self.stdout[-1] .= a:data[0]
call extend(self.stdout, a:data[1:])
endfunction
<
See https://gist.github.com/lambdalisue/818c389f5602bdc2bee4f93bb9ebef12
for benchmark test for removing trailing CRs.
"on_exit" callback will be called with "exitval" argument which indicate
the exit status of the process.
If you need exit status in asynchronous way, use this callback like example
code in |Vital.System.Job-usage|.
If you need it in synchronous way, use |Vital.System.Job-instance.wait()|
method.
Note that "on_stdout" and/or "on_stderr" callbacks will be called prior to
the "on_exit" callback if defined. So "on_exit" timing is a bit different
from "exit_cb" in Vim 8 while this library waits "close_cb" when "on_stdout"
and/or "on_stderr" are defined.
-----------------------------------------------------------------------------
PROMISE *Vital.System.Job-promise*
The following example code uses |Vital.Async.Promise| to wrap process.
Not like examples in Async.Promise, the following works on both Vim and
Neovim.
>
let s:Job = vital#vital#import('System.Job')
let s:Promise = vital#vital#import('Async.Promise')
function! s:on_receive(buffer, data) abort dict
let a:buffer[-1] .= a:data[0]
call extend(a:buffer, a:data[1:])
endfunction
function! s:clone(url) abort
let stdout = ['']
let stderr = ['']
return s:Promise.new({
\ rv, rj -> s:Job.start(['git', 'clone', a:url], {
\ 'on_stdout': function('s:on_receive', [stdout]),
\ 'on_stderr': function('s:on_receive', [stderr]),
\ 'on_exit': { e ->
\ e ? rj(join(stderr, "\n")) : rv(join(stdout, "\n"))
\ }
\ })
\})
endfunction
" Usage
call s:clone('https://github.com/lambdalisue/gina.vim')
\.then({ result -> execute('echo ' . string(result), '') })
\.catch({ result -> execute('echo ' . string(result), '') })
=============================================================================
FUNCTION *Vital.System.Job-function*
*Vital.System.Job.is_available()*
is_available()
Return 1 if this library is available on current Vim session.
*Vital.System.Job.start()*
start({args}[, {options}])
Start a new process and return a job instance.
The {args} must be a |List| instance and the process will be executed
directly. If you would like to execute process through the shell,
see below.
See jobstart (Vim) or job_start (Neovim) for more detail about this
behavior.
The following attributes are allowed in {options}.
"cwd" A current working directory of the job. Defaults to
|current-directory|.
Note that this option requires Vim 8.0.0902 or Neovim
"env" An environment variable dictionary of the job.
Note that this option requires Vim 8.0.0902 or Neovim
0.5.0.
"on_stdout" A callback called when the process output to stdout.
"on_stderr" A callback called when the process output to stderr.
"on_exit" A callback called when the process terminated.
Note that all attributes in the {options} is exposed to the job
instance and users can access that in callbacks by |self| while the
callbacks above are called as a dictionary function of the job
instance.
See |Vital.System.Job-callbacks| for detail of each callbacks.
Note that this library prohibit |String| {args} while Vim and Neovim
treat that a bit differently. So to execute process through the
shell, use 'shell' and 'shellcmdflag' like
>
call s:Job.start(split(&shell) + split(&shellcmdflag) + ['{cmd}'])
<
=============================================================================
INSTANCE *Vital.System.Job-instance*
*Vital.System.Job-instance.pid()*
pid()
Return a process id.
It keeps returning a same process id even when the process has
terminated.
*Vital.System.Job-instance.status()*
status()
Return a status of the process. The value is one of
"run" The process is running.
"dead" The process has terminated or failed to open.
*Vital.System.Job-instance.send()*
send({data})
Send {data} to the process. The {data} may be a string or a list.
When {data} is a list, it is assumed as a newline separated list and
all newline character in each item is replaced to "" in Vim and "\0"
in Neovim.
Note that no extra newline is appended to the {data}, mean that users
need to add it by themselves.
>
call job.send('abc' . "\n")
" -> send: abc\n
call job.send(['abc', '123\n456', ''])
" -> send: abc\n123\045\n (Vim: abc\n123456\n)
<
*Vital.System.Job-instance.close()*
close()
Close "stdin" channel/stream of the process. In other word, it sends
EOF to the process (e.g. "cat" command in Linux requires EOF.)
Note that once "stdin" has closed, there is no way to re-open.
*Vital.System.Job-instance.stop()*
stop()
Stop the process by sending SIGTERM. If the process does not
terminate after a timeout then SIGKILL will be sent.
Note that the above "timeout" is
- 2000 ms in Vim (defined in System.Job.Vim)
- 2000 ms in Neovim (defined in src/nvim/event/process.c)
While "timeout" in Neovim is defined in it's C code, it might be
changed in the future so developers should not rely on the value.
*Vital.System.Job-instance.wait()*
wait([{timeout}])
Wait until the process has terminated or {timeout} has passed.
If {timeout} is v:null (default), it waits forever until the process
has terminated.
It immediately returns an exit status if the process has terminated.
Otherwise it waits the process and returns an exit status or
-1 The timeout has passed
-2 The process is interrupted
The unit of {timeout} is milliseconds.
When user hit <C-c>, the process will terminate.
Note that Vim and Neovim return different exitval on different OS
when |Vital.System.Job-instance.stop()| has called prior to this
method.
So developers should avoid checking the return status when they
called |Vital.System.Job-instance.stop()|.
=============================================================================
vim:tw=78:fo=tcq2mM:ts=8:ft=help:norl