To: vim_dev@googlegroups.com Subject: Patch 7.4.1341 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1341 Problem: It's difficult to add more arguments to ch_sendraw() and ch_sendexpr(). Solution: Make the third option a dictionary. Files: src/eval.c, src/structs.h, src/channel.c, src/os_unix.c, src/os_win32.c, src/proto/channel.pro, src/testdir/test_channel.vim, runtime/doc/eval.txt *** ../vim-7.4.1340/src/eval.c 2016-02-16 19:37:24.445233506 +0100 --- src/eval.c 2016-02-16 20:53:30.309425831 +0100 *************** *** 9930,9944 **** } /* ! * Get the "mode" entry from "dict", if it exists, and parse the mode name. ! * If the mode is invalide return FAIL. */ static int ! get_mode_arg(dict_T *dict, jobopt_T *opt) { dictitem_T *item; char_u *mode; if ((item = dict_find(dict, (char_u *)"mode", -1)) != NULL) { mode = get_tv_string(&item->di_tv); --- 9930,9947 ---- } /* ! * Get the option entries from "dict", and parse them. ! * If an option value is invalid return FAIL. */ static int ! get_job_options(dict_T *dict, jobopt_T *opt) { dictitem_T *item; char_u *mode; + if (dict == NULL) + return OK; + if ((item = dict_find(dict, (char_u *)"mode", -1)) != NULL) { mode = get_tv_string(&item->di_tv); *************** *** 9956,9961 **** --- 9959,9975 ---- return FAIL; } } + + if ((item = dict_find(dict, (char_u *)"callback", -1)) != NULL) + { + opt->jo_callback = get_callback(&item->di_tv); + if (opt->jo_callback == NULL) + { + EMSG2(_(e_invarg2), "callback"); + return FAIL; + } + } + return OK; } *************** *** 9966,9972 **** f_ch_open(typval_T *argvars, typval_T *rettv) { char_u *address; - char_u *callback = NULL; char_u *p; char *rest; int port; --- 9980,9985 ---- *************** *** 10004,10023 **** } options.jo_mode = MODE_JSON; if (argvars[1].v_type == VAR_DICT) { dict_T *dict = argvars[1].vval.v_dict; dictitem_T *item; /* parse argdict */ ! if (get_mode_arg(dict, &options) == FAIL) return; if ((item = dict_find(dict, (char_u *)"waittime", -1)) != NULL) waittime = get_tv_number(&item->di_tv); if ((item = dict_find(dict, (char_u *)"timeout", -1)) != NULL) timeout = get_tv_number(&item->di_tv); - if ((item = dict_find(dict, (char_u *)"callback", -1)) != NULL) - callback = get_callback(&item->di_tv); } if (waittime < 0 || timeout < 0) { --- 10017,10035 ---- } options.jo_mode = MODE_JSON; + options.jo_callback = NULL; if (argvars[1].v_type == VAR_DICT) { dict_T *dict = argvars[1].vval.v_dict; dictitem_T *item; /* parse argdict */ ! if (get_job_options(dict, &options) == FAIL) return; if ((item = dict_find(dict, (char_u *)"waittime", -1)) != NULL) waittime = get_tv_number(&item->di_tv); if ((item = dict_find(dict, (char_u *)"timeout", -1)) != NULL) timeout = get_tv_number(&item->di_tv); } if (waittime < 0 || timeout < 0) { *************** *** 10029,10038 **** if (channel != NULL) { rettv->vval.v_channel = channel; ! channel_set_mode(channel, options.jo_mode); channel_set_timeout(channel, timeout); - if (callback != NULL && *callback != NUL) - channel_set_callback(channel, callback); } } --- 10041,10048 ---- if (channel != NULL) { rettv->vval.v_channel = channel; ! channel_set_options(channel, &options); channel_set_timeout(channel, timeout); } } *************** *** 10082,10087 **** --- 10092,10098 ---- { channel_T *channel; char_u *callback = NULL; + jobopt_T options; channel = get_channel_arg(&argvars[0]); if (channel == NULL) *************** *** 10089,10097 **** if (argvars[2].v_type != VAR_UNKNOWN) { ! callback = get_callback(&argvars[2]); ! if (callback == NULL) return NULL; } /* Set the callback. An empty callback means no callback and not reading * the response. */ --- 10100,10114 ---- if (argvars[2].v_type != VAR_UNKNOWN) { ! if (argvars[2].v_type != VAR_DICT) ! { ! EMSG(_(e_invarg)); return NULL; + } + options.jo_callback = NULL; + if (get_job_options(argvars[2].vval.v_dict, &options) == FAIL) + return NULL; + callback = options.jo_callback; } /* Set the callback. An empty callback means no callback and not reading * the response. */ *************** *** 14511,14527 **** /* Default mode is NL. */ options.jo_mode = MODE_NL; if (argvars[1].v_type != VAR_UNKNOWN) { - dict_T *dict; - if (argvars[1].v_type != VAR_DICT) { EMSG(_(e_invarg)); return; } ! dict = argvars[1].vval.v_dict; ! if (get_mode_arg(dict, &options) == FAIL) return; } --- 14528,14542 ---- /* Default mode is NL. */ options.jo_mode = MODE_NL; + options.jo_callback = NULL; if (argvars[1].v_type != VAR_UNKNOWN) { if (argvars[1].v_type != VAR_DICT) { EMSG(_(e_invarg)); return; } ! if (get_job_options(argvars[1].vval.v_dict, &options) == FAIL) return; } *** ../vim-7.4.1340/src/structs.h 2016-02-16 19:25:07.580925715 +0100 --- src/structs.h 2016-02-16 20:34:24.021499966 +0100 *************** *** 1373,1383 **** }; /* ! * Options for job commands. */ typedef struct { ! ch_mode_T jo_mode; } jobopt_T; --- 1373,1384 ---- }; /* ! * Options for job and channel commands. */ typedef struct { ! ch_mode_T jo_mode; /* "mode" */ ! char_u *jo_callback; /* "callback", not allocated! */ } jobopt_T; *** ../vim-7.4.1340/src/channel.c 2016-02-16 19:25:07.580925715 +0100 --- src/channel.c 2016-02-16 20:43:11.751946370 +0100 *************** *** 697,702 **** --- 697,714 ---- } /* + * Set various properties from an "options" argument. + */ + void + channel_set_options(channel_T *channel, jobopt_T *options) + { + channel_set_mode(channel, options->jo_mode); + + if (options->jo_callback != NULL && *options->jo_callback != NUL) + channel_set_callback(channel, options->jo_callback); + } + + /* * Set the callback for channel "channel" for the response with "id". */ void *** ../vim-7.4.1340/src/os_unix.c 2016-02-16 19:44:14.732951080 +0100 --- src/os_unix.c 2016-02-16 20:44:19.763229009 +0100 *************** *** 5127,5133 **** # ifdef FEAT_CHANNEL channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]); channel_set_job(channel, job); ! channel_set_mode(channel, options->jo_mode); # ifdef FEAT_GUI channel_gui_register(channel); # endif --- 5127,5133 ---- # ifdef FEAT_CHANNEL channel_set_pipes(channel, fd_in[1], fd_out[0], fd_err[0]); channel_set_job(channel, job); ! channel_set_options(channel, options); # ifdef FEAT_GUI channel_gui_register(channel); # endif *** ../vim-7.4.1340/src/os_win32.c 2016-02-16 20:31:27.223345909 +0100 --- src/os_win32.c 2016-02-16 20:45:31.702470333 +0100 *************** *** 5125,5131 **** job->jv_channel = channel; channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]); channel_set_job(channel, job); ! channel_set_mode(channel, options->jo_mode); # ifdef FEAT_GUI channel_gui_register(channel); --- 5125,5131 ---- job->jv_channel = channel; channel_set_pipes(channel, (sock_T)ifd[1], (sock_T)ofd[0], (sock_T)efd[0]); channel_set_job(channel, job); ! channel_set_options(channel, options); # ifdef FEAT_GUI channel_gui_register(channel); *** ../vim-7.4.1340/src/proto/channel.pro 2016-02-16 19:25:07.580925715 +0100 --- src/proto/channel.pro 2016-02-16 20:43:52.371517915 +0100 *************** *** 7,15 **** channel_T *channel_open(char *hostname, int port_in, int waittime, void (*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_mode(channel_T *channel, ch_mode_T ch_mode); void channel_set_timeout(channel_T *channel, int timeout); void channel_set_callback(channel_T *channel, char_u *callback); void channel_set_req_callback(channel_T *channel, char_u *callback, int id); char_u *channel_get(channel_T *channel); int channel_collapse(channel_T *channel); --- 7,16 ---- channel_T *channel_open(char *hostname, int port_in, int waittime, void (*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_mode(channel_T *channel, ch_mode_T mode); void channel_set_timeout(channel_T *channel, int timeout); void channel_set_callback(channel_T *channel, char_u *callback); + void channel_set_options(channel_T *channel, jobopt_T *options); void channel_set_req_callback(channel_T *channel, char_u *callback, int id); char_u *channel_get(channel_T *channel); int channel_collapse(channel_T *channel); *** ../vim-7.4.1340/src/testdir/test_channel.vim 2016-02-16 19:25:07.584925674 +0100 --- src/testdir/test_channel.vim 2016-02-16 20:57:00.983206848 +0100 *************** *** 117,123 **** call assert_equal('added more', getline('$')) " Send a request with a specific handler. ! call ch_sendexpr(handle, 'hello!', 's:RequestHandler') sleep 10m if !exists('s:responseHandle') call assert_false(1, 's:responseHandle was not set') --- 117,123 ---- call assert_equal('added more', getline('$')) " Send a request with a specific handler. ! call ch_sendexpr(handle, 'hello!', {'callback': 's:RequestHandler'}) sleep 10m if !exists('s:responseHandle') call assert_false(1, 's:responseHandle was not set') *************** *** 128,134 **** unlet s:responseHandle let s:responseMsg = '' ! call ch_sendexpr(handle, 'hello!', function('s:RequestHandler')) sleep 10m if !exists('s:responseHandle') call assert_false(1, 's:responseHandle was not set') --- 128,134 ---- unlet s:responseHandle let s:responseMsg = '' ! call ch_sendexpr(handle, 'hello!', {'callback': function('s:RequestHandler')}) sleep 10m if !exists('s:responseHandle') call assert_false(1, 's:responseHandle was not set') *************** *** 171,177 **** call assert_equal('ok', ch_sendexpr(handle, 'empty-request')) " make the server quit, can't check if this works, should not hang. ! call ch_sendexpr(handle, '!quit!', 0) endfunc func Test_communicate() --- 171,177 ---- call assert_equal('ok', ch_sendexpr(handle, 'empty-request')) " make the server quit, can't check if this works, should not hang. ! call ch_sendexpr(handle, '!quit!', {'callback': 0}) endfunc func Test_communicate() *************** *** 242,248 **** call assert_equal('we called you', s:reply) " Test that it works while not waiting on a numbered message. ! call ch_sendexpr(handle, 'call me again', 0) sleep 10m call assert_equal('we did call you', s:reply) endfunc --- 242,248 ---- call assert_equal('we called you', s:reply) " Test that it works while not waiting on a numbered message. ! call ch_sendexpr(handle, 'call me again', {'callback': 0}) sleep 10m call assert_equal('we did call you', s:reply) endfunc *************** *** 292,302 **** call assert_equal("run", job_status(job)) try let handle = job_getchannel(job) ! call ch_sendraw(handle, "echo something\n", 0) let msg = ch_readraw(handle) call assert_equal("something\n", substitute(msg, "\r", "", 'g')) ! call ch_sendraw(handle, "double this\n", 0) let msg = ch_readraw(handle) call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) --- 292,302 ---- call assert_equal("run", job_status(job)) try let handle = job_getchannel(job) ! call ch_sendraw(handle, "echo something\n", {'callback': 0}) let msg = ch_readraw(handle) call assert_equal("something\n", substitute(msg, "\r", "", 'g')) ! call ch_sendraw(handle, "double this\n", {'callback': 0}) let msg = ch_readraw(handle) call assert_equal("this\nAND this\n", substitute(msg, "\r", "", 'g')) *************** *** 315,324 **** call assert_equal("run", job_status(job)) try let handle = job_getchannel(job) ! call ch_sendraw(handle, "echo something\n", 0) call assert_equal("something", ch_readraw(handle)) ! call ch_sendraw(handle, "double this\n", 0) call assert_equal("this", ch_readraw(handle)) call assert_equal("AND this", ch_readraw(handle)) --- 315,324 ---- call assert_equal("run", job_status(job)) try let handle = job_getchannel(job) ! call ch_sendraw(handle, "echo something\n", {'callback': 0}) call assert_equal("something", ch_readraw(handle)) ! call ch_sendraw(handle, "double this\n", {'callback': 0}) call assert_equal("this", ch_readraw(handle)) call assert_equal("AND this", ch_readraw(handle)) *************** *** 340,346 **** " Test that "unlet handle" in a handler doesn't crash Vim. func s:unlet_handle(port) let s:channelfd = ch_open('localhost:' . a:port, s:chopt) ! call ch_sendexpr(s:channelfd, "test", function('s:UnletHandler')) sleep 10m call assert_equal('what?', s:unletResponse) endfunc --- 340,346 ---- " Test that "unlet handle" in a handler doesn't crash Vim. func s:unlet_handle(port) let s:channelfd = ch_open('localhost:' . a:port, s:chopt) ! call ch_sendexpr(s:channelfd, "test", {'callback': function('s:UnletHandler')}) sleep 10m call assert_equal('what?', s:unletResponse) endfunc *************** *** 360,366 **** " Test that "unlet handle" in a handler doesn't crash Vim. func s:close_handle(port) let s:channelfd = ch_open('localhost:' . a:port, s:chopt) ! call ch_sendexpr(s:channelfd, "test", function('s:CloseHandler')) sleep 10m call assert_equal('what?', s:unletResponse) endfunc --- 360,366 ---- " Test that "unlet handle" in a handler doesn't crash Vim. func s:close_handle(port) let s:channelfd = ch_open('localhost:' . a:port, s:chopt) ! call ch_sendexpr(s:channelfd, "test", {'callback': function('s:CloseHandler')}) sleep 10m call assert_equal('what?', s:unletResponse) endfunc *** ../vim-7.4.1340/runtime/doc/eval.txt 2016-02-16 14:07:36.186482678 +0100 --- runtime/doc/eval.txt 2016-02-16 20:48:54.952327482 +0100 *************** *** 1809,1818 **** ch_logfile( {fname} [, {mode}]) none start logging channel activity ch_open( {address} [, {argdict})] Number open a channel to {address} ch_readraw( {handle}) String read from channel {handle} ! ch_sendexpr( {handle}, {expr} [, {callback}]) any send {expr} over JSON channel {handle} ! ch_sendraw( {handle}, {string} [, {callback}]) any send {string} over raw channel {handle} changenr() Number current change number char2nr( {expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} cindent( {lnum}) Number C indent for line {lnum} --- 1821,1831 ---- ch_logfile( {fname} [, {mode}]) none start logging channel activity ch_open( {address} [, {argdict})] Number open a channel to {address} ch_readraw( {handle}) String read from channel {handle} ! ch_sendexpr( {handle}, {expr} [, {options}]) any send {expr} over JSON channel {handle} ! ch_sendraw( {handle}, {string} [, {options}]) any send {string} over raw channel {handle} + ch_status( {handle}) String status of channel {handle} changenr() Number current change number char2nr( {expr}[, {utf8}]) Number ASCII/UTF8 value of first char in {expr} cindent( {lnum}) Number C indent for line {lnum} *************** *** 1945,1950 **** --- 1958,1964 ---- isdirectory( {directory}) Number TRUE if {directory} is a directory islocked( {expr}) Number TRUE if {expr} is locked items( {dict}) List key-value pairs in {dict} + job_getchannel( {job}) Number get the channel handle for {job} job_start( {command} [, {options}]) Job start a job job_status( {job}) String get the status of a job job_stop( {job} [, {how}]) Number stop a job *************** *** 2671,2679 **** ch_logfile( {fname} [, {mode}]) *ch_logfile()* Start logging channel activity to {fname}. When {mode} is omitted or "a" append to the file. When {mode} is "w" start with an empty file. ! When {fname} is an empty string: stop logging. ch_open({address} [, {argdict}]) *ch_open()* Open a channel to {address}. See |channel|. --- 2688,2700 ---- ch_logfile( {fname} [, {mode}]) *ch_logfile()* Start logging channel activity to {fname}. + When {fname} is an empty string: stop logging. + When {mode} is omitted or "a" append to the file. When {mode} is "w" start with an empty file. ! ! The file is flushed after every message, on Unix you can use ! "tail -f" to see what is going on in real time. ch_open({address} [, {argdict}]) *ch_open()* Open a channel to {address}. See |channel|. *************** *** 2704,2734 **** within that time an empty string is returned. TODO: depends on channel mode. ! ch_sendexpr({handle}, {expr} [, {callback}]) *ch_sendexpr()* Send {expr} over channel {handle}. The {expr} is encoded according to the type of channel. The function cannot be used with a raw channel. See |channel-use|. *E912* ! When {callback} is given returns immediately. Without ! {callback} waits for a response and returns the decoded ! expression. When there is an error or timeout returns an ! empty string. ! When {callback} is zero no response is expected. ! Otherwise {callback} must be a Funcref or the name of a ! function. It is called when the response is received. See ! |channel-callback|. {only available when compiled with the |+channel| feature} ! ch_sendraw({handle}, {string} [, {callback}]) *ch_sendraw()* Send {string} over channel {handle}. Works like |ch_sendexpr()|, but does not encode the request or decode the response. The caller is responsible for the ! correct contents. See |channel-use|. {only available when compiled with the |+channel| feature} *copy()* copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't different from using {expr} directly. --- 2725,2765 ---- within that time an empty string is returned. TODO: depends on channel mode. ! ch_sendexpr({handle}, {expr} [, {options}]) *ch_sendexpr()* Send {expr} over channel {handle}. The {expr} is encoded according to the type of channel. The function cannot be used with a raw channel. See |channel-use|. *E912* ! {options} must be a Dictionary. ! When "callback" is a Funcref or the name of a function, ! ch_sendexpr() returns immediately. The callback is invoked ! when the response is received. See |channel-callback|. ! ! Without "callback" ch_sendexpr() waits for a response and ! returns the decoded expression. When there is an error or ! timeout it returns an empty string. ! When "callback" is zero no response is expected. {only available when compiled with the |+channel| feature} ! ch_sendraw({handle}, {string} [, {options}]) *ch_sendraw()* Send {string} over channel {handle}. Works like |ch_sendexpr()|, but does not encode the request or decode the response. The caller is responsible for the ! correct contents. Also does not add a newline for a channel ! in NL mode, the caller must do that. The NL in the response ! is removed. ! See |channel-use|. {only available when compiled with the |+channel| feature} + ch_status({handle}) *ch_status()* + Return the status of channel {handle}: + "fail" failed to open the channel + "open" channel can be used + "closed" channel can not be used + *copy()* copy({expr}) Make a copy of {expr}. For Numbers and Strings this isn't different from using {expr} directly. *************** *** 4263,4269 **** order. ! job_start({command} [, {options}]) *job_start()* Start a job and return a Job object. Unlike |system()| and |:!cmd| this does not wait for the job to finish. --- 4353,4363 ---- order. ! job_getchannel({job}) *job_getchannel()* ! Get the channel handle that {job} is using. ! {only available when compiled with the |+job| feature} ! ! job_start({command} [, {options}]) *job_start()* Start a job and return a Job object. Unlike |system()| and |:!cmd| this does not wait for the job to finish. *** ../vim-7.4.1340/src/version.c 2016-02-16 20:31:27.223345909 +0100 --- src/version.c 2016-02-16 20:57:36.194836056 +0100 *************** *** 749,750 **** --- 749,752 ---- { /* Add new patch number below this line */ + /**/ + 1341, /**/ -- Facepalm statement #5: "Petrol getting more expensive? Not for me, I'm always tanking for 20 dollars" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///