Job_control

Nvim :help 頁面,使用 tree-sitter-vimdoc 解析器,從 原始碼 產生


Nvim 工作控制 job-control
工作控制是在 Nvim 中執行多工的一種方式,因此腳本可以產生並控制多個進程,而不會阻塞當前的 Nvim 實例。

概念

工作 ID job-id
每個工作都由一個整數 ID 識別,該 ID 在當前 Nvim 會話的生命週期內是唯一的。每個工作 ID 都是一個有效的 channel-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 啟動,該 -c 執行命令(一個迴圈,印出 0 到 9),然後結束。
OnEvent() 回呼函數會傳遞給 jobstart(),以處理各種工作事件。它會顯示從 shell 接收到的標準輸出/標準錯誤資料。
有關 on_stdouton_stderr 的資訊,請參閱 channel-callbackon_exit
傳遞給 on_exit 回呼函數的參數:0:job-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()
主目錄
指令索引
快速參考