# HG changeset patch # User Christian Brabandt # Date 1456230607 -3600 # Node ID b717dae2f26d397a081503621c3aaac204690b94 # Parent 30b7880d752224b6e303fb5cc49d11133ee1eb09 commit https://github.com/vim/vim/commit/4e221c99e85ed40c98892068a01270b9e7492d98 Author: Bram Moolenaar Date: Tue Feb 23 13:20:22 2016 +0100 patch 7.4.1398 Problem: The close-cb option is not implemented yet. Solution: Implemente close-cb. (Yasuhiro Matsumoto) diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -485,7 +485,11 @@ static char *e_cannot_connect = N_("E902 * Returns NULL for failure. */ channel_T * -channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)) +channel_open( + char *hostname, + int port_in, + int waittime, + void (*nb_close_cb)(void)) { int sd = -1; struct sockaddr_in server; @@ -711,7 +715,7 @@ channel_open(char *hostname, int port_in } channel->CH_SOCK_FD = (sock_T)sd; - channel->ch_close_cb = close_cb; + channel->ch_nb_close_cb = nb_close_cb; #ifdef FEAT_GUI channel_gui_register(channel); @@ -790,6 +794,15 @@ channel_set_options(channel_T *channel, else *cbp = NULL; } + if (opt->jo_set & JO_CLOSE_CALLBACK) + { + cbp = &channel->ch_close_cb; + vim_free(*cbp); + if (opt->jo_close_cb != NULL && *opt->jo_close_cb != NUL) + *cbp = vim_strsave(opt->jo_close_cb); + else + *cbp = NULL; + } } /* @@ -1255,7 +1268,7 @@ may_invoke_callback(channel_T *channel, ch_mode_T ch_mode = channel->ch_part[part].ch_mode; char_u *callback = NULL; - if (channel->ch_close_cb != NULL) + if (channel->ch_nb_close_cb != NULL) /* this channel is handled elsewhere (netbeans) */ return FALSE; @@ -1477,7 +1490,28 @@ channel_close(channel_T *channel) } #endif - channel->ch_close_cb = NULL; + if (channel->ch_close_cb != NULL) + { + typval_T argv[1]; + typval_T rettv; + int dummy; + + /* invoke the close callback; increment the refcount to avoid it + * being freed halfway */ + argv[0].v_type = VAR_CHANNEL; + argv[0].vval.v_channel = channel; + ++channel->ch_refcount; + call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb), + &rettv, 1, argv, 0L, 0L, &dummy, TRUE, NULL); + clear_tv(&rettv); + --channel->ch_refcount; + + /* the callback is only called once */ + vim_free(channel->ch_close_cb); + channel->ch_close_cb = NULL; + } + + channel->ch_nb_close_cb = NULL; channel_clear(channel); } @@ -1539,6 +1573,8 @@ channel_clear(channel_T *channel) #endif vim_free(channel->ch_callback); channel->ch_callback = NULL; + vim_free(channel->ch_close_cb); + channel->ch_close_cb = NULL; } #if defined(EXITFREE) || defined(PROTO) @@ -1732,8 +1768,8 @@ channel_read(channel_T *channel, int par * keep stdin and stderr open? Probably not, assume the other side * has died. */ channel_close(channel); - if (channel->ch_close_cb != NULL) - (*channel->ch_close_cb)(); + if (channel->ch_nb_close_cb != NULL) + (*channel->ch_nb_close_cb)(); if (len < 0) { diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -10067,6 +10067,18 @@ get_job_options(typval_T *tv, jobopt_T * return FAIL; } } + else if (STRCMP(hi->hi_key, "close-cb") == 0) + { + if (!(supported & JO_CLOSE_CALLBACK)) + break; + opt->jo_set |= JO_CLOSE_CALLBACK; + opt->jo_close_cb = get_callback(item); + if (opt->jo_close_cb == NULL) + { + EMSG2(_(e_invarg2), "close-cb"); + return FAIL; + } + } else if (STRCMP(hi->hi_key, "waittime") == 0) { if (!(supported & JO_WAITTIME)) @@ -18924,7 +18936,7 @@ item_compare2(const void *s1, const void rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ res = call_func(sortinfo->item_compare_func, - (int)STRLEN(sortinfo->item_compare_func), + (int)STRLEN(sortinfo->item_compare_func), &rettv, 2, argv, 0L, 0L, &dummy, TRUE, sortinfo->item_compare_selfdict); clear_tv(&argv[0]); diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -7,10 +7,10 @@ channel_T *add_channel(void); void channel_free(channel_T *channel); void channel_gui_register(channel_T *channel); void channel_gui_register_all(void); -channel_T *channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)); +channel_T *channel_open(char *hostname, int port_in, int waittime, void (*nb_close_cb)(void)); void channel_set_pipes(channel_T *channel, sock_T in, sock_T out, sock_T err); void channel_set_job(channel_T *channel, job_T *job); -void channel_set_options(channel_T *channel, jobopt_T *options); +void channel_set_options(channel_T *channel, jobopt_T *opt); void channel_set_req_callback(channel_T *channel, int part, char_u *callback, int id); char_u *channel_get(channel_T *channel, int part); int channel_collapse(channel_T *channel, int part); diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1366,9 +1366,12 @@ struct channel_S { * first error until the connection works * again. */ - void (*ch_close_cb)(void); /* callback for when channel is closed */ + void (*ch_nb_close_cb)(void); + /* callback for Netbeans when channel is + * closed */ char_u *ch_callback; /* call when any msg is not handled */ + char_u *ch_close_cb; /* call when channel is closed */ job_T *ch_job; /* Job that uses this channel; this does not * count as a reference to avoid a circular @@ -1377,25 +1380,27 @@ struct channel_S { int ch_refcount; /* reference count */ }; -#define JO_MODE 0x0001 /* channel mode */ -#define JO_IN_MODE 0x0002 /* stdin mode */ -#define JO_OUT_MODE 0x0004 /* stdout mode */ -#define JO_ERR_MODE 0x0008 /* stderr mode */ -#define JO_CALLBACK 0x0010 /* channel callback */ -#define JO_OUT_CALLBACK 0x0020 /* stdout callback */ -#define JO_ERR_CALLBACK 0x0040 /* stderr callback */ -#define JO_WAITTIME 0x0080 /* only for ch_open() */ -#define JO_TIMEOUT 0x0100 /* all timeouts */ -#define JO_OUT_TIMEOUT 0x0200 /* stdout timeouts */ -#define JO_ERR_TIMEOUT 0x0400 /* stderr timeouts */ -#define JO_PART 0x0800 /* "part" */ -#define JO_ID 0x1000 /* "id" */ -#define JO_STOPONEXIT 0x2000 /* "stoponexit" */ -#define JO_EXIT_CB 0x4000 /* "exit-cb" */ -#define JO_ALL 0xffffff +#define JO_MODE 0x0001 /* channel mode */ +#define JO_IN_MODE 0x0002 /* stdin mode */ +#define JO_OUT_MODE 0x0004 /* stdout mode */ +#define JO_ERR_MODE 0x0008 /* stderr mode */ +#define JO_CALLBACK 0x0010 /* channel callback */ +#define JO_OUT_CALLBACK 0x0020 /* stdout callback */ +#define JO_ERR_CALLBACK 0x0040 /* stderr callback */ +#define JO_CLOSE_CALLBACK 0x0080 /* close callback */ +#define JO_WAITTIME 0x0100 /* only for ch_open() */ +#define JO_TIMEOUT 0x0200 /* all timeouts */ +#define JO_OUT_TIMEOUT 0x0400 /* stdout timeouts */ +#define JO_ERR_TIMEOUT 0x0800 /* stderr timeouts */ +#define JO_PART 0x1000 /* "part" */ +#define JO_ID 0x2000 /* "id" */ +#define JO_STOPONEXIT 0x4000 /* "stoponexit" */ +#define JO_EXIT_CB 0x8000 /* "exit-cb" */ +#define JO_ALL 0xffffff #define JO_MODE_ALL (JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE) -#define JO_CB_ALL (JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK) +#define JO_CB_ALL \ + (JO_CALLBACK + JO_OUT_CALLBACK + JO_ERR_CALLBACK + JO_CLOSE_CALLBACK) #define JO_TIMEOUT_ALL (JO_TIMEOUT + JO_OUT_TIMEOUT + JO_ERR_TIMEOUT) /* @@ -1412,6 +1417,7 @@ typedef struct char_u *jo_callback; /* not allocated! */ char_u *jo_out_cb; /* not allocated! */ char_u *jo_err_cb; /* not allocated! */ + char_u *jo_close_cb; /* not allocated! */ int jo_waittime; int jo_timeout; int jo_out_timeout; diff --git a/src/testdir/test_channel.py b/src/testdir/test_channel.py --- a/src/testdir/test_channel.py +++ b/src/testdir/test_channel.py @@ -140,6 +140,10 @@ class ThreadedTCPRequestHandler(socketse print("sending: {}".format(cmd)) self.request.sendall(cmd.encode('utf-8')) response = "" + elif decoded[1] == 'close me': + print("closing") + self.request.close() + response = "" elif decoded[1] == 'wait a bit': time.sleep(0.2) response = "waited" diff --git a/src/testdir/test_channel.vim b/src/testdir/test_channel.vim --- a/src/testdir/test_channel.vim +++ b/src/testdir/test_channel.vim @@ -471,9 +471,9 @@ endfunc """"""""" -let s:job_ret = 'not yet' +let s:job_exit_ret = 'not yet' function MyExitCb(job, status) - let s:job_ret = 'done' + let s:job_exit_ret = 'done' endfunc function s:test_exit_callback(port) @@ -490,6 +490,32 @@ func Test_exit_callback() " calling job_status() triggers the callback call job_status(s:exit_job) - call assert_equal('done', s:job_ret) + call assert_equal('done', s:job_exit_ret) endif endfunc + +""""""""" + +let s:ch_close_ret = 'alive' +function MyCloseCb(ch) + let s:ch_close_ret = 'closed' +endfunc + +function s:test_close_callback(port) + let handle = ch_open('localhost:' . a:port, s:chopt) + if ch_status(handle) == "fail" + call assert_false(1, "Can't open channel") + return + endif + call ch_setoptions(handle, {'close-cb': 'MyCloseCb'}) + + call assert_equal('', ch_sendexpr(handle, 'close me')) + sleep 20m + call assert_equal('closed', s:ch_close_ret) +endfunc + +func Test_close_callback() + call ch_log('Test_close_callback()') + call s:run_server('s:test_close_callback') +endfunc + diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -749,6 +749,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1398, +/**/ 1397, /**/ 1396,