作业控制

Nvim 的 :help 页面, 源代码 使用 tree-sitter-vimdoc 解析器生成。


Nvim 作业控制 作业控制
作业控制是 Nvim 中执行多任务的一种方式,因此脚本可以生成和控制多个进程,而不会阻塞当前的 Nvim 实例。

概念

作业 ID 作业 ID
每个作业由一个整型 ID 标识,在当前 Nvim 会话的整个生命周期内都是唯一的。每个作业 ID 都是一个有效的 通道 ID:它们共享相同的“键空间”。诸如 jobstart() 之类的函数返回作业 ID;诸如 jobstop()chansend()rpcnotify()rpcrequest() 之类的函数则接受作业 ID。
作业的标准输入输出流构成一个 通道,该通道可以发送和接收原始字节或 msgpack-rpc 消息。
要控制作业,请使用“job…”函数族:jobstart()jobstop() 等。
示例
function! s:OnEvent(job_id, data, event) dict
  if a:event == 'stdout'
    let str = self.shell.' stdout: '.join(a:data)
  elseif a:event == 'stderr'
    let str = self.shell.' stderr: '.join(a:data)
  else
    let str = self.shell.' exited'
  endif
  call append(line('$'), str)
endfunction
let s:callbacks = {
\ 'on_stdout': function('s:OnEvent'),
\ 'on_stderr': function('s:OnEvent'),
\ 'on_exit': function('s:OnEvent')
\ }
let job1 = jobstart(['bash'], extend({'shell': 'shell 1'}, s:callbacks))
let job2 = jobstart(['bash', '-c', 'for i in {1..10}; do echo hello $i!; sleep 1; done'], extend({'shell': 'shell 2'}, s:callbacks))
要测试上述脚本,请将其复制到 ~/foo.vim 文件中并运行它
nvim -u ~/foo.vim
对发生情况的描述
两个 bash shell 通过 jobstart() 生成,它们的标准输入/输出/错误流连接到 nvim。
第一个 shell 处于空闲状态,等待从其标准输入读取命令。
第二个 shell 使用 -c 启动,该命令执行命令(一个打印 0 到 9 的 for 循环),然后退出。
OnEvent() 回调传递给 jobstart() 以处理各种作业事件。它显示从 shell 收到的标准输出/错误数据。
有关 on_stdouton_stderr,请参阅 通道回调on_exit
传递给 on_exit 回调的参数:0:作业 ID 1:进程的退出代码,或 128+SIGNUM(如果由信号导致,例如,SIGTERM 为 143)。2:事件类型:“exit”。
注意: 尚未被发送方刷新的缓冲的标准输出/错误数据不会触发 on_stdout/on_stderr 回调(但如果进程结束,则会调用 on_exit 回调)。例如,"ruby -e" 会缓冲输出,因此除非启用了“自动刷新”($stdout.sync=true),否则较小的字符串会被缓冲。
function! Receive(job_id, data, event)
  echom printf('%s: %s',a:event,string(a:data))
endfunction
call jobstart(['ruby', '-e',
  \ '$stdout.sync = true; 5.times do sleep 1 and puts "Hello Ruby!" end'],
  \ {'on_stdout': 'Receive'})
注意 2:作业事件处理程序可能会收到部分(不完整)行。对于 on_stdout/on_stderr 的给定调用,a:data 不保证以换行符结尾。
abcdefg 可能会以 ['abc']['defg'] 的形式到达。
abc\nefg 可能会以 ['abc', '']['efg']['abc']['','efg'],甚至 ['ab']['c','efg'] 的形式到达。处理此问题的一种简单方法是将列表初始化为 [''],然后按如下方式追加到列表中
let s:chunks = ['']
func! s:on_stdout(job_id, data, event) dict
  let s:chunks[-1] .= a:data[0]
  call extend(s:chunks, a:data[1:])
endf
jobstart-options 字典作为 self 传递给回调。以上示例可以用这种“面向对象”的风格编写
let Shell = {}
function Shell.on_stdout(_job_id, data, event)
  call append(line('$'),
        \ printf('[%s] %s: %s', a:event, self.name, join(a:data[:-2])))
endfunction
let Shell.on_stderr = function(Shell.on_stdout)
function Shell.on_exit(job_id, _data, event)
  let msg = printf('job %d ("%s") finished', a:job_id, self.name)
  call append(line('$'), printf('[%s] BOOM!', a:event))
  call append(line('$'), printf('[%s] %s!', a:event, msg))
endfunction
function Shell.new(name, cmd)
  let object = extend(copy(g:Shell), {'name': a:name})
  let object.cmd = ['sh', '-c', a:cmd]
  let object.id = jobstart(object.cmd, object)
  $
  return object
endfunction
let instance = Shell.new('bomb',
      \ 'for i in $(seq 9 -1 1); do echo $i 1>&$((i % 2 + 1)); sleep 1; done')
要向作业的标准输入发送数据,请使用 chansend()
:call chansend(job1, "ls\n")
:call chansend(job1, "invalid-command\n")
:call chansend(job1, "exit\n")
可以使用 jobstop() 函数随时终止作业。
:call jobstop(job1)
可以在不终止作业的情况下关闭单个流,请参阅 chanclose()
主要
命令索引
快速参考