job_control.txt (5211B)
1 *job_control.txt* Nvim 2 3 4 NVIM REFERENCE MANUAL by Thiago de Arruda 5 6 7 Nvim job control *job* *job-control* 8 9 Job control is a way to perform multitasking in Nvim, so scripts can spawn and 10 control multiple processes without blocking the current Nvim instance. 11 12 Type |gO| to see the table of contents. 13 14 ============================================================================== 15 Concepts 16 17 Job Id *job-id* 18 19 Each job is identified by an integer id, unique for the life of the current 20 Nvim session. Each job-id is a valid |channel-id|: they share the same "key 21 space". Functions like |jobstart()| return job ids; functions like 22 |jobstop()|, |chansend()|, |rpcnotify()|, and |rpcrequest()| take job ids. 23 24 Job stdio streams form a |channel| which can send and receive raw bytes or 25 |msgpack-rpc| messages. 26 27 ============================================================================== 28 Usage *job-control-usage* 29 30 To control jobs, use the "job…" family of functions: |jobstart()|, 31 |jobstop()|, etc. 32 33 Example: >vim 34 35 function! s:OnEvent(job_id, data, event) dict 36 if a:event == 'stdout' 37 let str = self.shell.' stdout: '.join(a:data) 38 elseif a:event == 'stderr' 39 let str = self.shell.' stderr: '.join(a:data) 40 else 41 let str = self.shell.' exited' 42 endif 43 44 call append(line('$'), str) 45 endfunction 46 let s:callbacks = { 47 \ 'on_stdout': function('s:OnEvent'), 48 \ 'on_stderr': function('s:OnEvent'), 49 \ 'on_exit': function('s:OnEvent') 50 \ } 51 let job1 = jobstart(['bash'], extend({'shell': 'shell 1'}, s:callbacks)) 52 let job2 = jobstart(['bash', '-c', 'for i in {1..10}; do echo hello $i!; sleep 1; done'], extend({'shell': 'shell 2'}, s:callbacks)) 53 54 To test the above script, copy it to a file ~/foo.vim and run it: >bash 55 nvim -u ~/foo.vim 56 < 57 Description of what happens: 58 - Two bash shells are spawned by |jobstart()| with their stdin/stdout/stderr 59 streams connected to nvim. 60 - The first shell is idle, waiting to read commands from its stdin. 61 - The second shell is started with -c which executes the command (a for-loop 62 printing 0 through 9) and then exits. 63 - `OnEvent()` callback is passed to |jobstart()| to handle various job 64 events. It displays stdout/stderr data received from the shells. 65 66 For |on_stdout| and |on_stderr| see |channel-callback|. 67 *on_exit* 68 Arguments passed to on_exit callback: 69 0: |job-id| 70 1: Exit-code of the process, or 128+SIGNUM if by signal (e.g. 143 on SIGTERM). 71 2: Event type: "exit" 72 73 74 Note: Buffered stdout/stderr data which has not been flushed by the sender 75 will not trigger the on_stdout/on_stderr callback (but if the process 76 ends, the on_exit callback will be invoked). 77 For example, "ruby -e" buffers output, so small strings will be 78 buffered unless "auto-flushing" ($stdout.sync=true) is enabled. >vim 79 function! Receive(job_id, data, event) 80 echom printf('%s: %s',a:event,string(a:data)) 81 endfunction 82 call jobstart(['ruby', '-e', 83 \ '$stdout.sync = true; 5.times do sleep 1 and puts "Hello Ruby!" end'], 84 \ {'on_stdout': 'Receive'}) 85 < https://github.com/neovim/neovim/issues/1592 86 87 Note 2: 88 Job event handlers may receive partial (incomplete) lines. For a given 89 invocation of on_stdout/on_stderr, `a:data` is not guaranteed to end 90 with a newline. 91 - `abcdefg` may arrive as `['abc']`, `['defg']`. 92 - `abc\nefg` may arrive as `['abc', '']`, `['efg']` or `['abc']`, 93 `['','efg']`, or even `['ab']`, `['c','efg']`. 94 Easy way to deal with this: initialize a list as `['']`, then append 95 to it as follows: >vim 96 let s:chunks = [''] 97 func! s:on_stdout(job_id, data, event) dict 98 let s:chunks[-1] .= a:data[0] 99 call extend(s:chunks, a:data[1:]) 100 endf 101 < 102 103 The |jobstart-options| dictionary is passed as |self| to the callback. 104 The above example could be written in this "object-oriented" style: >vim 105 106 let Shell = {} 107 108 function Shell.on_stdout(_job_id, data, event) 109 call append(line('$'), 110 \ printf('[%s] %s: %s', a:event, self.name, join(a:data[:-2]))) 111 endfunction 112 113 let Shell.on_stderr = function(Shell.on_stdout) 114 115 function Shell.on_exit(job_id, _data, event) 116 let msg = printf('job %d ("%s") finished', a:job_id, self.name) 117 call append(line('$'), printf('[%s] BOOM!', a:event)) 118 call append(line('$'), printf('[%s] %s!', a:event, msg)) 119 endfunction 120 121 function Shell.new(name, cmd) 122 let object = extend(copy(g:Shell), {'name': a:name}) 123 let object.cmd = ['sh', '-c', a:cmd] 124 let object.id = jobstart(object.cmd, object) 125 $ 126 return object 127 endfunction 128 129 let instance = Shell.new('bomb', 130 \ 'for i in $(seq 9 -1 1); do echo $i 1>&$((i % 2 + 1)); sleep 1; done') 131 < 132 To send data to the job's stdin, use |chansend()|: >vim 133 :call chansend(job1, "ls\n") 134 :call chansend(job1, "invalid-command\n") 135 :call chansend(job1, "exit\n") 136 < 137 A job may be killed at any time with the |jobstop()| function: 138 >vim 139 :call jobstop(job1) 140 < 141 Individual streams can be closed without killing the job, see |chanclose()|. 142 143 vim:tw=78:ts=8:noet:ft=help:norl: