mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 7.4.1380
Problem: The job exit callback is not implemented. Solution: Add the "exit-cb" option.
This commit is contained in:
@@ -833,6 +833,8 @@ invoke_callback(channel_T *channel, char_u *callback, typval_T *argv)
|
|||||||
|
|
||||||
call_func(callback, (int)STRLEN(callback),
|
call_func(callback, (int)STRLEN(callback),
|
||||||
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
|
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
|
||||||
|
clear_tv(&rettv);
|
||||||
|
|
||||||
/* If an echo command was used the cursor needs to be put back where
|
/* If an echo command was used the cursor needs to be put back where
|
||||||
* it belongs. */
|
* it belongs. */
|
||||||
setcursor();
|
setcursor();
|
||||||
|
122
src/eval.c
122
src/eval.c
@@ -7774,6 +7774,7 @@ job_free(job_T *job)
|
|||||||
job->jv_prev->jv_next = job->jv_next;
|
job->jv_prev->jv_next = job->jv_next;
|
||||||
|
|
||||||
vim_free(job->jv_stoponexit);
|
vim_free(job->jv_stoponexit);
|
||||||
|
vim_free(job->jv_exit_cb);
|
||||||
vim_free(job);
|
vim_free(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7781,7 +7782,13 @@ job_free(job_T *job)
|
|||||||
job_unref(job_T *job)
|
job_unref(job_T *job)
|
||||||
{
|
{
|
||||||
if (job != NULL && --job->jv_refcount <= 0)
|
if (job != NULL && --job->jv_refcount <= 0)
|
||||||
job_free(job);
|
{
|
||||||
|
/* Do not free the job when it has not ended yet and there is a
|
||||||
|
* "stoponexit" flag or an exit callback. */
|
||||||
|
if (job->jv_status != JOB_STARTED
|
||||||
|
|| (job->jv_stoponexit == NULL && job->jv_exit_cb == NULL))
|
||||||
|
job_free(job);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -7819,6 +7826,14 @@ job_set_options(job_T *job, jobopt_T *opt)
|
|||||||
else
|
else
|
||||||
job->jv_stoponexit = vim_strsave(opt->jo_stoponexit);
|
job->jv_stoponexit = vim_strsave(opt->jo_stoponexit);
|
||||||
}
|
}
|
||||||
|
if (opt->jo_set & JO_EXIT_CB)
|
||||||
|
{
|
||||||
|
vim_free(job->jv_exit_cb);
|
||||||
|
if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL)
|
||||||
|
job->jv_exit_cb = NULL;
|
||||||
|
else
|
||||||
|
job->jv_exit_cb = vim_strsave(opt->jo_exit_cb);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -7830,7 +7845,7 @@ job_stop_on_exit()
|
|||||||
job_T *job;
|
job_T *job;
|
||||||
|
|
||||||
for (job = first_job; job != NULL; job = job->jv_next)
|
for (job = first_job; job != NULL; job = job->jv_next)
|
||||||
if (job->jv_stoponexit != NULL && *job->jv_stoponexit != NUL)
|
if (job->jv_status == JOB_STARTED && job->jv_stoponexit != NULL)
|
||||||
mch_stop_job(job, job->jv_stoponexit);
|
mch_stop_job(job, job->jv_stoponexit);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -10030,7 +10045,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
|
|||||||
opt->jo_out_cb = get_callback(item);
|
opt->jo_out_cb = get_callback(item);
|
||||||
if (opt->jo_out_cb == NULL)
|
if (opt->jo_out_cb == NULL)
|
||||||
{
|
{
|
||||||
EMSG2(_(e_invarg2), "out-db");
|
EMSG2(_(e_invarg2), "out-cb");
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -10108,6 +10123,18 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (STRCMP(hi->hi_key, "exit-cb") == 0)
|
||||||
|
{
|
||||||
|
if (!(supported & JO_EXIT_CB))
|
||||||
|
break;
|
||||||
|
opt->jo_set |= JO_EXIT_CB;
|
||||||
|
opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf);
|
||||||
|
if (opt->jo_ecb_buf == NULL)
|
||||||
|
{
|
||||||
|
EMSG2(_(e_invarg2), "exit-cb");
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
--todo;
|
--todo;
|
||||||
@@ -14771,7 +14798,7 @@ f_items(typval_T *argvars, typval_T *rettv)
|
|||||||
dict_list(argvars, rettv, 2);
|
dict_list(argvars, rettv, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef FEAT_JOB
|
#if defined(FEAT_JOB) || defined(PROTO)
|
||||||
/*
|
/*
|
||||||
* Get the job from the argument.
|
* Get the job from the argument.
|
||||||
* Returns NULL if the job is invalid.
|
* Returns NULL if the job is invalid.
|
||||||
@@ -14824,7 +14851,7 @@ f_job_setoptions(typval_T *argvars, typval_T *rettv UNUSED)
|
|||||||
if (job == NULL)
|
if (job == NULL)
|
||||||
return;
|
return;
|
||||||
clear_job_options(&opt);
|
clear_job_options(&opt);
|
||||||
if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT) == FAIL)
|
if (get_job_options(&argvars[1], &opt, JO_STOPONEXIT + JO_EXIT_CB) == FAIL)
|
||||||
return;
|
return;
|
||||||
job_set_options(job, &opt);
|
job_set_options(job, &opt);
|
||||||
}
|
}
|
||||||
@@ -14858,7 +14885,8 @@ f_job_start(typval_T *argvars UNUSED, typval_T *rettv)
|
|||||||
clear_job_options(&opt);
|
clear_job_options(&opt);
|
||||||
opt.jo_mode = MODE_NL;
|
opt.jo_mode = MODE_NL;
|
||||||
if (get_job_options(&argvars[1], &opt,
|
if (get_job_options(&argvars[1], &opt,
|
||||||
JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL + JO_STOPONEXIT) == FAIL)
|
JO_MODE_ALL + JO_CB_ALL + JO_TIMEOUT_ALL
|
||||||
|
+ JO_STOPONEXIT + JO_EXIT_CB) == FAIL)
|
||||||
return;
|
return;
|
||||||
job_set_options(job, &opt);
|
job_set_options(job, &opt);
|
||||||
|
|
||||||
@@ -14958,6 +14986,77 @@ theend:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Get the status of "job" and invoke the exit callback when needed.
|
||||||
|
* The returned string is not allocated.
|
||||||
|
*/
|
||||||
|
static char *
|
||||||
|
job_status(job_T *job)
|
||||||
|
{
|
||||||
|
char *result;
|
||||||
|
|
||||||
|
if (job->jv_status == JOB_ENDED)
|
||||||
|
/* No need to check, dead is dead. */
|
||||||
|
result = "dead";
|
||||||
|
else if (job->jv_status == JOB_FAILED)
|
||||||
|
result = "fail";
|
||||||
|
else
|
||||||
|
{
|
||||||
|
result = mch_job_status(job);
|
||||||
|
# ifdef FEAT_CHANNEL
|
||||||
|
if (job->jv_status == JOB_ENDED)
|
||||||
|
ch_log(job->jv_channel, "Job ended");
|
||||||
|
# endif
|
||||||
|
if (job->jv_status == JOB_ENDED && job->jv_exit_cb != NULL)
|
||||||
|
{
|
||||||
|
typval_T argv[3];
|
||||||
|
typval_T rettv;
|
||||||
|
int dummy;
|
||||||
|
|
||||||
|
/* invoke the exit callback */
|
||||||
|
argv[0].v_type = VAR_JOB;
|
||||||
|
argv[0].vval.v_job = job;
|
||||||
|
argv[1].v_type = VAR_NUMBER;
|
||||||
|
argv[1].vval.v_number = job->jv_exitval;
|
||||||
|
call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
|
||||||
|
&rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
|
||||||
|
clear_tv(&rettv);
|
||||||
|
}
|
||||||
|
if (job->jv_status == JOB_ENDED && job->jv_refcount == 0)
|
||||||
|
{
|
||||||
|
/* The job already was unreferenced, now that it ended it can be
|
||||||
|
* freed. Careful: caller must not use "job" after this! */
|
||||||
|
job_free(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called once in a while: check if any jobs with an "exit-cb" have ended.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
job_check_ended()
|
||||||
|
{
|
||||||
|
static time_t last_check = 0;
|
||||||
|
time_t now;
|
||||||
|
job_T *job;
|
||||||
|
job_T *next;
|
||||||
|
|
||||||
|
/* Only do this once in 10 seconds. */
|
||||||
|
now = time(NULL);
|
||||||
|
if (last_check + 10 < now)
|
||||||
|
{
|
||||||
|
last_check = now;
|
||||||
|
for (job = first_job; job != NULL; job = next)
|
||||||
|
{
|
||||||
|
next = job->jv_next;
|
||||||
|
if (job->jv_status == JOB_STARTED && job->jv_exit_cb != NULL)
|
||||||
|
job_status(job); /* may free "job" */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* "job_status()" function
|
* "job_status()" function
|
||||||
*/
|
*/
|
||||||
@@ -14969,13 +15068,7 @@ f_job_status(typval_T *argvars, typval_T *rettv)
|
|||||||
|
|
||||||
if (job != NULL)
|
if (job != NULL)
|
||||||
{
|
{
|
||||||
if (job->jv_status == JOB_ENDED)
|
result = job_status(job);
|
||||||
/* No need to check, dead is dead. */
|
|
||||||
result = "dead";
|
|
||||||
else if (job->jv_status == JOB_FAILED)
|
|
||||||
result = "fail";
|
|
||||||
else
|
|
||||||
result = mch_job_status(job);
|
|
||||||
rettv->v_type = VAR_STRING;
|
rettv->v_type = VAR_STRING;
|
||||||
rettv->vval.v_string = vim_strsave((char_u *)result);
|
rettv->vval.v_string = vim_strsave((char_u *)result);
|
||||||
}
|
}
|
||||||
@@ -22857,7 +22950,8 @@ copy_tv(typval_T *from, typval_T *to)
|
|||||||
case VAR_JOB:
|
case VAR_JOB:
|
||||||
#ifdef FEAT_JOB
|
#ifdef FEAT_JOB
|
||||||
to->vval.v_job = from->vval.v_job;
|
to->vval.v_job = from->vval.v_job;
|
||||||
++to->vval.v_job->jv_refcount;
|
if (to->vval.v_job != NULL)
|
||||||
|
++to->vval.v_job->jv_refcount;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case VAR_CHANNEL:
|
case VAR_CHANNEL:
|
||||||
|
@@ -317,6 +317,6 @@
|
|||||||
# define PLINES_NOFILL(x) plines(x)
|
# define PLINES_NOFILL(x) plines(x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(FEAT_CHANNEL) || defined(FEAT_CLIENTSERVER)
|
#if defined(FEAT_CHANNEL) || defined(FEAT_JOB) || defined(FEAT_CLIENTSERVER)
|
||||||
# define MESSAGE_QUEUE
|
# define MESSAGE_QUEUE
|
||||||
#endif
|
#endif
|
||||||
|
@@ -6256,5 +6256,9 @@ parse_queued_messages(void)
|
|||||||
/* Process the queued clientserver messages. */
|
/* Process the queued clientserver messages. */
|
||||||
server_parse_messages();
|
server_parse_messages();
|
||||||
# endif
|
# endif
|
||||||
|
# ifdef FEAT_JOB
|
||||||
|
/* Check if any jobs have ended. */
|
||||||
|
job_check_ended();
|
||||||
|
# endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@@ -87,6 +87,7 @@ char_u *get_expr_name(expand_T *xp, int idx);
|
|||||||
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict);
|
int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict);
|
||||||
int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv);
|
int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv);
|
||||||
void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
|
void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
|
||||||
|
void job_check_ended(void);
|
||||||
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
|
void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
|
||||||
float_T vim_round(float_T f);
|
float_T vim_round(float_T f);
|
||||||
long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
|
long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
|
||||||
|
@@ -1265,6 +1265,7 @@ struct jobvar_S
|
|||||||
#endif
|
#endif
|
||||||
jobstatus_T jv_status;
|
jobstatus_T jv_status;
|
||||||
char_u *jv_stoponexit; /* allocated */
|
char_u *jv_stoponexit; /* allocated */
|
||||||
|
char_u *jv_exit_cb; /* allocated */
|
||||||
|
|
||||||
int jv_refcount; /* reference count */
|
int jv_refcount; /* reference count */
|
||||||
channel_T *jv_channel; /* channel for I/O, reference counted */
|
channel_T *jv_channel; /* channel for I/O, reference counted */
|
||||||
@@ -1390,6 +1391,7 @@ struct channel_S {
|
|||||||
#define JO_PART 0x0800 /* "part" */
|
#define JO_PART 0x0800 /* "part" */
|
||||||
#define JO_ID 0x1000 /* "id" */
|
#define JO_ID 0x1000 /* "id" */
|
||||||
#define JO_STOPONEXIT 0x2000 /* "stoponexit" */
|
#define JO_STOPONEXIT 0x2000 /* "stoponexit" */
|
||||||
|
#define JO_EXIT_CB 0x4000 /* "exit-cb" */
|
||||||
#define JO_ALL 0xffffff
|
#define JO_ALL 0xffffff
|
||||||
|
|
||||||
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
|
#define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
|
||||||
@@ -1418,6 +1420,8 @@ typedef struct
|
|||||||
int jo_id;
|
int jo_id;
|
||||||
char_u jo_soe_buf[NUMBUFLEN];
|
char_u jo_soe_buf[NUMBUFLEN];
|
||||||
char_u *jo_stoponexit;
|
char_u *jo_stoponexit;
|
||||||
|
char_u jo_ecb_buf[NUMBUFLEN];
|
||||||
|
char_u *jo_exit_cb;
|
||||||
} jobopt_T;
|
} jobopt_T;
|
||||||
|
|
||||||
|
|
||||||
|
@@ -468,3 +468,28 @@ func Test_call()
|
|||||||
call ch_log('Test_call()')
|
call ch_log('Test_call()')
|
||||||
call s:run_server('s:test_call')
|
call s:run_server('s:test_call')
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
"""""""""
|
||||||
|
|
||||||
|
let s:job_ret = 'not yet'
|
||||||
|
function MyExitCb(job, status)
|
||||||
|
let s:job_ret = 'done'
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
function s:test_exit_callback(port)
|
||||||
|
call job_setoptions(s:job, {'exit-cb': 'MyExitCb'})
|
||||||
|
let s:exit_job = s:job
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_exit_callback()
|
||||||
|
if has('job')
|
||||||
|
call s:run_server('s:test_exit_callback')
|
||||||
|
|
||||||
|
" the job may take a little while to exit
|
||||||
|
sleep 50m
|
||||||
|
|
||||||
|
" calling job_status() triggers the callback
|
||||||
|
call job_status(s:exit_job)
|
||||||
|
call assert_equal('done', s:job_ret)
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
@@ -747,6 +747,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
1380,
|
||||||
/**/
|
/**/
|
||||||
1379,
|
1379,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user