job_control.txt 5.2 KB

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