job util for vim8
and neovim
features / why another remake:
more convenient for caller and impl
fallback to system()
if no job impl available
supply your own impl easily by setting g:ZFVimJobImpl
many useful util functions
ZFGroupJobStart
)ZFAsyncRun
)ZFJobOutput
)
statusline
async (ZFStatuslineLog
)ZFLogWinAdd
)ZFPopupCreate
)ZFAutoScript
)plugins that based on this plugin:
if you like my work, check here for a list of my vim plugins, or buy me a coffee
ZFJobStart -
^ \
ZFAsyncRun -\ | \ / ZFStatuslineLog
ZFAutoScript - => - ZFGroupJobStart - => - ZFJobOutput - => - ZFLogWin
\ ZFPopup
the job control is fully modularized, and can be combined easily to achieve complex logic
one typical example:
you saved a file
=> auto build, auto deploy (ZFAutoScript)
=> manage complex build workflow, with dependency logic and multithreaded (ZFGroupJobStart)
=> auto show build output as popup (ZFJobOutput)
and one typical config:
" what this config do
" when you save files under `/path/to/LibA`:
" * build LibA
" when you save files under `/path/to/LibB`:
" * build LibB, copy to Proj, and chain auto script to build Proj
" when you save files under `/path/to/Proj`:
" * build LibC and LibD concurrently, build Proj, and run
let g:ZFAutoScript = {
\ '/path/to/LibA' : {
\ 'memo' : 'build LibA',
\ 'jobCmd' : 'make',
\ 'jobCwd' : '/path/to/LibA',
\ },
\ '/path/to/LibB' : {
\ 'jobList' : [
\ [
\ {
\ 'memo' : 'build LibB',
\ 'jobCmd' : 'make',
\ 'jobCwd' : '/path/to/LibB',
\ },
\ ],
\ [
\ {
\ 'memo' : 'copy build result to Proj, this would run after build LibB',
\ 'jobCmd' : 'cp "/path/to/LibB/libB.so" "/path/to/Proj/lib/libB.so"',
\ },
\ ],
\ [
\ {
\ 'memo' : 'chain auto script, build Proj automatically',
\ 'jobCmd' : ['ZFAutoScriptRun "/path/to/Proj"'],
\ },
\ ],
\ ],
\ },
\ '/path/to/Proj' : {
\ 'jobList' : [
\ [
\ {
\ 'memo' : 'jobs within same group, can be run concurrently',
\ 'jobCmd' : ['ZFAutoScriptRun "/path/to/LibC"'],
\ },
\ {
\ 'memo' : 'jobs within same group, can be run concurrently',
\ 'jobCmd' : ['ZFAutoScriptRun "/path/to/LibD"'],
\ },
\ ],
\ [
\ {
\ 'memo' : 'jobs in different group, would wait for prev group finish',
\ 'jobCmd' : 'make',
\ 'jobCwd' : '/path/to/Proj',
\ },
\ ],
\ [
\ {
\ 'memo' : 'automatically run Proj after build success',
\ 'jobCmd' : './build/a.out',
\ 'jobCwd' : '/path/to/Proj',
\ },
\ ],
\ ],
\ },
\ }
it may hard to config for first time, but trust me, it changes the life
use Vundle or any other plugin manager you like to install
Plugin 'ZSaberLv0/ZFVimJob'
Plugin 'ZSaberLv0/ZFVimPopup' " optional, support show job output as popup
start the job
function! s:onOutput(jobStatus, textList, type)
endfunction
function! s:onExit(jobStatus, exitCode)
endfunction
let jobId = ZFJobStart({
\ 'jobCmd' : 'your job cmd',
\ 'onOutput' : function('s:onOutput'),
\ 'onExit' : function('s:onExit'),
\ 'jobEncoding' : '',
\ 'jobTimeout' : 0,
\ })
onExit
would be called when:
0
ZFJobStop(jobId)
, exitCode would be g:ZFJOBSTOP
start multiple jobs
function! s:group1_job1(jobStatus)
" note you may chain vim functions by setting `jobCmd` to vim function
" no async support for vim functions, though
call doSomeHeavyWork()
endfunction
function! s:groupJobOnExit(groupJobStatus, exitCode)
endfunction
let groupJobId = ZFGroupJobStart({
\ 'jobList' : [
\ [
\ {'jobCmd' : 'group0_job0'},
\ {'jobCmd' : 'group0_job1'},
\ ...
\ ],
\ [
\ {'jobCmd' : 'group1_job0'},
\ {'jobCmd' : function('s:group1_job1')},
\ ],
\ ...
\ ],
\ 'onExit' : function('s:groupJobOnExit'),
\ 'jobTimeout' : 0,
\ })
group1_xxx
would start only after previous group0_xxx
's jobs
all finished with 0
exitCode
group's onExit
would be called when:
0
0
exitCode, group job's exitCode would be the child's exitCodeZFGroupJobStop(groupJobId)
, group job's exitCode would be g:ZFJOBSTOP
here's some plugins that used ZFVimJob to simplify complex job control:
ZFJobAvailable()
ZFJobStart(jobCmd_or_jobOption)
jobOption: {
'jobCmd' : 'job cmd',
// jobCmd can be:
// * string, shell command to run as job
// * vim `function(jobStatus)` or any callable object to `ZFJobFuncCall()`,
// return `{output:xxx, exitCode:0}` to indicate invoke result,
// if none, it's considered as success
// * number, use `timer_start()` to delay,
// has better performance than starting a `sleep` job
'jobCwd' : 'optional, cwd to run the job',
'onLog' : 'optional, func(jobStatus, log)',
'onOutputFilter' : 'optional, func(jobStatus, textList, type[stdout/stderr]), modify textList or empty to discard',
'onOutput' : 'optional, func(jobStatus, textList, type[stdout/stderr])',
'onEnter' : 'optional, func(jobStatus)',
'onExit' : 'optional, func(jobStatus, exitCode)',
'jobOutputDelay' : 'optional, default is g:ZFJobOutputDelay',
'jobOutputLimit' : 'optional, max line of jobOutput that would be stored in jobStatus, default is 2000',
'jobEncoding' : 'optional, if supplied, encoding conversion would be made before passing output textList',
'jobTimeout' : 'optional, if supplied, ZFJobStop would be called with g:ZFJOBTIMEOUT',
'jobFallback' : 'optional, true by default, whether fallback to `system()` if no job impl available',
'jobImplData' : {}, // optional, if supplied, merge to jobStatus['jobImplData']
}
ZFJobStop(jobId)
ZFJobSend(jobId, text)
ZFJobStatus(jobId)
{
'jobId' : -1,
'jobOption' : {},
'jobOutput' : [],
'jobImplData' : {},
}
ZFJobTaskMap()
ZFJobInfo(jobStatus)
ZFJobLog(jobId, log)
ZFGroupJobStart(groupJobOption)
groupJobOption: {
'jobList' : [
[
{
'jobCmd' : '',
'onOutput' : '',
'onExit' : '',
...
},
{
'jobList' : [ // group job can be chained
...
],
},
...
],
...
],
'jobCmd' : 'optional, used only when jobList not supplied',
'jobCwd' : 'optional, if supplied, would use as default value for child ZFJobStart',
'onLog' : 'optional, func(groupJobStatus, log)',
'onOutputFilter' : 'optional, func(groupJobStatus, textList, type[stdout/stderr]), modify textList or empty to discard',
'onOutput' : 'optional, func(groupJobStatus, textList, type[stdout/stderr])',
'onEnter' : 'optional, func(groupJobStatus)',
'onExit' : 'optional, func(groupJobStatus, exitCode)',
'jobOutputDelay' : 'optional, default is g:ZFJobOutputDelay',
'jobOutputLimit' : 'optional, max line of jobOutput that would be stored in jobStatus, default is 2000',
'jobEncoding' : 'optional, if supplied, would use as default value for child ZFJobStart',
'jobTimeout' : 'optional, if supplied, would use as default value for child ZFJobStart',
'jobFallback' : 'optional, if supplied, would use as default value for child ZFJobStart',
'jobImplData' : {}, // optional, if supplied, merge to groupJobStatus['jobImplData']
'groupJobTimeout' : 'optional, if supplied, ZFGroupJobStop would be called with g:ZFJOBTIMEOUT',
'onJobLog' : 'optional, func(groupJobStatus, jobStatus, log)',
'onJobOutput' : 'optional, func(groupJobStatus, jobStatus, textList, type[stdout/stderr])',
'onJobExit' : 'optional, func(groupJobStatus, jobStatus, exitCode)',
}
ZFGroupJobStop(groupJobId)
ZFGroupJobSend(groupJobId, text)
ZFGroupJobStatus(groupJobId)
groupJobStatus : {
'jobId' : '',
'jobOption' : {},
'jobOutput' : [],
'jobStatusFailed' : {},
'jobIndex' : 0,
'jobStatusList' : [[{jobStatus}], [{jobStatus}, {jobStatus}]],
'jobImplData' : {},
}
child jobStatus jobImplData: {
'groupJobId' : '',
'groupJobChildState' : '1: running, 0: successFinished, -1: failed',
'groupJobChildIndex' : 0,
'groupJobChildSubIndex' : 0,
}
ZFGroupJobTaskMap()
ZFGroupJobInfo(groupJobStatus)
ZFGroupJobLog(groupJobId, log)
since low version vim doesn't support function(func, argList)
,
we supply a wrapper to simulate:
ZFJobFunc(func[, argList])
: return a Dictionary that can be run by ZFJobFuncCall(jobFunc, argList)
func can be:
vim function('name')
vim 7.4
or above, function('s:func')
can be usedvim 7.3
or below, you must put it in global scope, like function('Fn_func')
string or string list to :execute
function params can be accessed by a:000
series, example:
let Fn = ZFJobFunc([
\ 'let ret = Wrap(a:1, a:2, a:3, a:4)',
\ 'let ZFJobFuncRet = ret["xxx"]',
\ ], ['a', 'b'])
call ZFJobFuncCall(Fn, ['c', 'd'])
" Wrap() would be called as: Wrap('a', 'b', 'c', 'd')
to return values within the strings to :execute
, let ZFJobFuncRet = yourValue
ZFJobFuncCall(jobFunc, argList)
: run the function of ZFJobFunc()
ZFJobFuncInfo(jobFunc)
: return function info
and for timers:
ZFJobTimerStart(delay, ZFJobFunc(...))
ZFJobTimerStop(timerId)
and for interval (require has('timers')
):
ZFJobIntervalStart(interval, ZFJobFunc(...))
ZFJobIntervalStop(intervalId)
abstract job output is done by default
typically, what you needs to care
is the outputTo
option in your job option
you may also supply your own onOutput
, though
functions:
call ZFJobOutput(jobStatus, textList [, type(stdout/stderr)])
output accorrding to job's output configs:
jobStatus : {
'jobOption' : {
'outputTo' : {
'outputType' : 'statusline/logwin',
'outputId' : 'if exists, use this fixed outputId',
'outputInfo' : 'optional, text or function(jobStatus) which return text',
'outputInfoInterval' : 'if greater than 0, notify impl to update outputInfo with this interval',
'outputAutoCleanup' : 10000,
'outputManualCleanup' : 3000,
// extra config for actual impl
'statusline' : {...}, // see g:ZFStatuslineLog_defaultConfig
'logwin' : { // see g:ZFLogWin_defaultConfig
...
'logwinNoCloseWhenFocused' : 1,
'logwinAutoClosePreferHide' : 0,
},
'popup' : {...}, // see g:ZFPopup_defaultConfig
},
},
}
call ZFJobOutputCleanup(jobStatus)
manually cleanup output
call ZFStatuslineLog('your msg'[, timeout/option])
output logs to statusline, restore statusline automatically if set by other code or timeout,
or call ZFStatuslineLogClear()
to restore manually
call ZFLogWinAdd(logId, content)
output logs to a temp log window
control the log window:
call ZFLogWinShow(logId)
call ZFLogWinFocus(logId)
call ZFLogWinHide(logId)
call ZFLogWinRedraw(logId)
call ZFLogWinClear(logId)
call ZFLogWinClose(logId)
call ZFLogWinConfig(logId, option)
init or change log window config, the default option is:
let g:ZFLogWin_defaultConfig = {
\ 'newWinCmd' : 'rightbelow 5new',
\ 'filetype' : 'ZFLogWin',
\ 'statusline' : '',
\ 'makeDefaultKeymap' : 1,
\ 'initCallback' : '',
\ 'cleanupCallback' : '',
\ 'updateCallback' : '',
\ 'lazyUpdate' : 100,
\ 'maxLine' : '10000',
\ 'revertLines' : 0,
\ 'autoShow' : 1,
\ }
statusline
: string or function(logId)
makeDefaultKeymap
: if set, default q
to hide log windowinitCallback
/ cleanupCallback
/ updateCallback
: function(logId)
use ZSaberLv0/ZFVimPopup to show job output,
see g:ZFAutoScript_outputTo
for how to config
:ZFAsyncRun your cmd
or call ZFAsyncRun(jobCmd_or_jobOption[, taskNameOrJobId])
run shell async, output accorrding to ZFJobOutput
you may use call ZFAsyncRunStop([taskNameOrJobId])
to cancel,
or check full log by :ZFAsyncRunLog
options:
let g:ZFAsyncRun_outputTo = {...}
output log to where (see ZFJobOutput
), default:
let g:ZFAsyncRun_outputTo = {
\ 'outputType' : 'logwin',
\ 'outputInfo' : function('ZF_AsyncRunOutputInfo'),
\ 'logwin' : {
\ 'filetype' : 'ZFAsyncRunLog',
\ 'autoShow' : 1,
\ },
\ 'popup' : {
\ 'pos' : 'bottom',
\ 'width' : 1.0/3,
\ 'height' : 1.0/4,
\ 'x' : 1,
\ 'y' : 2,
\ 'wrap' : 1,
\ 'contentAlign' : 'bottom',
\ },
\ }
call ZFAutoScript(projDir, jobOption)
automaically run script when any file within projDir
was written (via FileWritePost
autocmd)
jobOption
can be jobCmd
or:
{ // jobOption passed to ZFAsyncRun
'autoScriptDelay' : 'optional, delay before run, 1 second by default',
}
use call ZFAutoScriptRemove([projDir])
to cancel,
use call ZFAutoScriptLog([projDir])
to get log,
use call ZFAutoScriptStatus([projDir])
for active config, which would return:
{
'projDir' : {}, // jobStatus
}
you may also use let g:ZFAutoScript={'projDir' : jobOption}
to config before plugin load
typical config:
let g:ZFAutoScript = {
\ '/YourProjPath' : {
\ 'jobCmd' : 'make',
\ 'jobCwd' : '/YourProjPath',
\ },
\ }
options:
let g:ZFAutoScript_outputTo = {...}
output log to where (see ZFJobOutput
), default:
let g:ZFAutoScript_outputTo = {
\ 'outputType' : 'popup',
\ 'outputId' : 'ZFAutoScript',
\ 'outputInfo' : function('ZF_AutoScriptOutputInfo'),
\ 'logwin' : {
\ 'newWinCmd' : '99wincmd l | vertical rightbelow 20new',
\ 'filetype' : 'ZFAutoScriptLog',
\ 'autoShow' : 1,
\ },
\ 'popup' : {
\ 'pos' : 'right|bottom',
\ 'width' : 1.0/3,
\ 'height' : 1.0/4,
\ 'x' : 1,
\ 'y' : 2,
\ 'wrap' : 0,
\ 'contentAlign' : 'bottom',
\ },
\ }
if any weird things happen, you may enable verbose log by:
let g:ZFJobVerboseLogEnable = 1
and dump the log to file:
:call writefile(g:ZFJobVerboseLog, 'log.txt')
by default, we support vim8
's job_start()
and neovim
's jobstart()
,
you may supply your own job impl by:
function! s:jobStart(jobStatus, onOutput, onExit)
let jobImplId = yourJobStart(...)
" store impl data if necessary
let a:jobStatus['jobImplData']['yourJobImplId'] = jobImplId
" return 1 if success or 0 if failed
return 1
endfunction
function! s:jobStop(jobStatus)
endfunction
function! s:jobSend(extraArgs0, extraArgs1, jobStatus, text)
endfunction
let g:ZFVimJobImpl = {
\ 'jobStart' : function('s:jobStart'),
\ 'jobStop' : function('s:jobStop'),
\ 'jobSend' : ZFJobFunc(function('s:jobSend'), [extraArgs0, extraArgs1]),
\ }
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。