To: vim_dev@googlegroups.com Subject: Patch 7.4.2298 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2298 Problem: It is not possible to close the "in" part of a channel. Solution: Add ch_close_in(). Files: src/evalfunc.c, src/channel.c, src/proto/channel.pro, src/testdir/test_channel.vim, runtime/doc/eval.txt, runtime/doc/channel.txt *** ../vim-7.4.2297/src/evalfunc.c 2016-08-29 22:48:12.125106388 +0200 --- src/evalfunc.c 2016-09-01 15:01:20.633367595 +0200 *************** *** 77,82 **** --- 77,83 ---- #endif #ifdef FEAT_JOB_CHANNEL static void f_ch_close(typval_T *argvars, typval_T *rettv); + static void f_ch_close_in(typval_T *argvars, typval_T *rettv); static void f_ch_evalexpr(typval_T *argvars, typval_T *rettv); static void f_ch_evalraw(typval_T *argvars, typval_T *rettv); static void f_ch_getbufnr(typval_T *argvars, typval_T *rettv); *************** *** 499,504 **** --- 500,506 ---- #endif #ifdef FEAT_JOB_CHANNEL {"ch_close", 1, 1, f_ch_close}, + {"ch_close_in", 1, 1, f_ch_close_in}, {"ch_evalexpr", 2, 3, f_ch_evalexpr}, {"ch_evalraw", 2, 3, f_ch_evalraw}, {"ch_getbufnr", 2, 2, f_ch_getbufnr}, *************** *** 1792,1797 **** --- 1794,1811 ---- } /* + * "ch_close()" function + */ + static void + f_ch_close_in(typval_T *argvars, typval_T *rettv UNUSED) + { + channel_T *channel = get_channel_arg(&argvars[0], TRUE, FALSE, 0); + + if (channel != NULL) + channel_close_in(channel); + } + + /* * "ch_getbufnr()" function */ static void *** ../vim-7.4.2297/src/channel.c 2016-09-01 14:35:19.306759126 +0200 --- src/channel.c 2016-09-01 14:51:50.506250892 +0200 *************** *** 2736,2741 **** --- 2736,2750 ---- } /* + * Close the "in" part channel "channel". + */ + void + channel_close_in(channel_T *channel) + { + may_close_part(&channel->CH_IN_FD); + } + + /* * Clear the read buffer on "channel"/"part". */ static void *** ../vim-7.4.2297/src/proto/channel.pro 2016-07-07 20:45:00.740424639 +0200 --- src/proto/channel.pro 2016-09-01 14:54:28.136900042 +0200 *************** *** 27,32 **** --- 27,33 ---- char *channel_status(channel_T *channel); void channel_info(channel_T *channel, dict_T *dict); void channel_close(channel_T *channel, int invoke_close_cb); + void channel_close_in(channel_T *channel); void channel_clear(channel_T *channel); void channel_free_all(void); char_u *channel_read_block(channel_T *channel, int part, int timeout); *** ../vim-7.4.2297/src/testdir/test_channel.vim 2016-09-01 14:35:19.306759126 +0200 --- src/testdir/test_channel.vim 2016-09-01 15:00:37.309738436 +0200 *************** *** 792,813 **** call Run_test_pipe_from_buffer(0) endfunc ! func Run_pipe_through_sort(all) if !executable('sort') || !has('job') return endif ! split sortin ! call setline(1, ['ccc', 'aaa', 'ddd', 'bbb', 'eee']) ! let options = {'in_io': 'buffer', 'in_name': 'sortin', ! \ 'out_io': 'buffer', 'out_name': 'sortout'} if !a:all let options.in_top = 2 let options.in_bot = 4 endif let g:job = job_start('sort', options) call assert_equal("run", job_status(g:job)) call WaitFor('job_status(g:job) == "dead"') call assert_equal("dead", job_status(g:job)) sp sortout call assert_equal('Reading from channel output...', getline(1)) if a:all --- 792,823 ---- call Run_test_pipe_from_buffer(0) endfunc ! func Run_pipe_through_sort(all, use_buffer) if !executable('sort') || !has('job') return endif ! let options = {'out_io': 'buffer', 'out_name': 'sortout'} ! if a:use_buffer ! split sortin ! call setline(1, ['ccc', 'aaa', 'ddd', 'bbb', 'eee']) ! let options.in_io = 'buffer' ! let options.in_name = 'sortin' ! endif if !a:all let options.in_top = 2 let options.in_bot = 4 endif let g:job = job_start('sort', options) call assert_equal("run", job_status(g:job)) + + if !a:use_buffer + call ch_sendraw(g:job, "ccc\naaa\nddd\nbbb\neee\n") + call ch_close_in(g:job) + endif + call WaitFor('job_status(g:job) == "dead"') call assert_equal("dead", job_status(g:job)) + sp sortout call assert_equal('Reading from channel output...', getline(1)) if a:all *************** *** 818,835 **** call job_stop(g:job) unlet g:job ! bwipe! sortin bwipe! sortout endfunc func Test_pipe_through_sort_all() call ch_log('Test_pipe_through_sort_all()') ! call Run_pipe_through_sort(1) endfunc func Test_pipe_through_sort_some() call ch_log('Test_pipe_through_sort_some()') ! call Run_pipe_through_sort(0) endfunc func Test_pipe_to_nameless_buffer() --- 828,852 ---- call job_stop(g:job) unlet g:job ! if a:use_buffer ! bwipe! sortin ! endif bwipe! sortout endfunc func Test_pipe_through_sort_all() call ch_log('Test_pipe_through_sort_all()') ! call Run_pipe_through_sort(1, 1) endfunc func Test_pipe_through_sort_some() call ch_log('Test_pipe_through_sort_some()') ! call Run_pipe_through_sort(0, 1) ! endfunc ! ! func Test_pipe_through_sort_feed() ! call ch_log('Test_pipe_through_sort_feed()') ! call Run_pipe_through_sort(1, 0) endfunc func Test_pipe_to_nameless_buffer() *** ../vim-7.4.2297/runtime/doc/eval.txt 2016-08-29 21:55:16.356528521 +0200 --- runtime/doc/eval.txt 2016-09-01 14:52:54.545702027 +0200 *************** *** 1973,1999 **** any call {func} with arguments {arglist} ceil({expr}) Float round {expr} up ch_close({handle}) none close {handle} ch_evalexpr({handle}, {expr} [, {options}]) any evaluate {expr} on JSON {handle} ch_evalraw({handle}, {string} [, {options}]) any evaluate {string} on raw {handle} ch_getbufnr({handle}, {what}) Number get buffer number for {handle}/{what} ch_getjob({channel}) Job get the Job of {channel} ! ch_info({handle}) String info about channel {handle} ch_log({msg} [, {handle}]) none write {msg} in the channel log file ch_logfile({fname} [, {mode}]) none start logging channel activity ch_open({address} [, {options}]) ! Channel open a channel to {address} ! ch_read({handle} [, {options}]) String read from {handle} ch_readraw({handle} [, {options}]) ! String read raw from {handle} ch_sendexpr({handle}, {expr} [, {options}]) any send {expr} over JSON {handle} ch_sendraw({handle}, {string} [, {options}]) any send {string} over raw {handle} ch_setoptions({handle}, {options}) none set options for {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} --- 2009,2036 ---- any call {func} with arguments {arglist} ceil({expr}) Float round {expr} up ch_close({handle}) none close {handle} + ch_close_in({handle}) none close in part of {handle} ch_evalexpr({handle}, {expr} [, {options}]) any evaluate {expr} on JSON {handle} ch_evalraw({handle}, {string} [, {options}]) any evaluate {string} on raw {handle} ch_getbufnr({handle}, {what}) Number get buffer number for {handle}/{what} ch_getjob({channel}) Job get the Job of {channel} ! ch_info({handle}) String info about channel {handle} ch_log({msg} [, {handle}]) none write {msg} in the channel log file ch_logfile({fname} [, {mode}]) none start logging channel activity ch_open({address} [, {options}]) ! Channel open a channel to {address} ! ch_read({handle} [, {options}]) String read from {handle} ch_readraw({handle} [, {options}]) ! String read raw from {handle} ch_sendexpr({handle}, {expr} [, {options}]) any send {expr} over JSON {handle} ch_sendraw({handle}, {string} [, {options}]) any send {string} over raw {handle} ch_setoptions({handle}, {options}) none set options for {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} *************** *** 2930,2951 **** :endif < In a GUI dialog, buttons are used. The layout of the buttons depends on the 'v' flag in 'guioptions'. If it is included, ! the buttons are always put vertically. Otherwise, confirm() tries to put the buttons in one horizontal line. If they don't fit, a vertical layout is used anyway. For some systems the horizontal layout is always used. ! ch_close({channel}) *ch_close()* ! Close {channel}. See |channel-close|. {only available when compiled with the |+channel| feature} ! ch_evalexpr({channel}, {expr} [, {options}]) *ch_evalexpr()* ! Send {expr} over {channel}. The {expr} is encoded according to the type of channel. The function cannot be used ! with a raw channel. See |channel-use|. *E912* *E917* {options} must be a Dictionary. It must not have a "callback" ! entry. ch_evalexpr() waits for a response and returns the decoded expression. When there is an error or timeout it returns an --- 2973,3006 ---- :endif < In a GUI dialog, buttons are used. The layout of the buttons depends on the 'v' flag in 'guioptions'. If it is included, ! the buttons are always put vertically. Otherwise, confirm() tries to put the buttons in one horizontal line. If they don't fit, a vertical layout is used anyway. For some systems the horizontal layout is always used. ! ch_close({handle}) *ch_close()* ! Close {handle}. See |channel-close|. ! {handle} can be Channel or a Job that has a Channel. ! A close callback is not invoked. ! {only available when compiled with the |+channel| feature} ! ch_close_in({handle}) *ch_close_in()* ! Close the "in" part of {handle}. See |channel-close-in|. ! {handle} can be Channel or a Job that has a Channel. ! A close callback is not invoked. ! ! {only available when compiled with the |+channel| feature} ! ! ch_evalexpr({handle}, {expr} [, {options}]) *ch_evalexpr()* ! Send {expr} over {handle}. The {expr} is encoded according to the type of channel. The function cannot be used ! with a raw channel. See |channel-use|. ! {handle} can be Channel or a Job that has a Channel. *E917* {options} must be a Dictionary. It must not have a "callback" ! entry. It can have a "timeout" entry to specify the timeout ! for this specific request. ch_evalexpr() waits for a response and returns the decoded expression. When there is an error or timeout it returns an *** ../vim-7.4.2297/runtime/doc/channel.txt 2016-08-26 17:58:33.590124381 +0200 --- runtime/doc/channel.txt 2016-09-01 14:50:09.963112813 +0200 *************** *** 490,495 **** --- 501,528 ---- time a line is added to the buffer, the last-but-one line will be send to the job stdin. This allows for editing the last line and sending it when pressing Enter. + *channel-close-in* + When not using the special mode the pipe or socket will be closed after the + last line has been written. This signals the reading end that the input + finished. You can also use |ch_close_in()| to close it sooner. + + NUL bytes in the text will be passed to the job (internally Vim stores these + as NL bytes). + + + Reading job output in the close callback ~ + *read-in-close-cb* + If the job can take some time and you don't need intermediate results, you can + add a close callback and read the output there: > + + func! CloseHandler(channel) + while ch_status(a:channel) == 'buffered' + echomsg ch_read(a:channel) + endwhile + endfunc + let job = job_start(command, {'close_cb': 'CloseHandler'}) + + You will want to do something more useful than "echomsg". ============================================================================== 9. Starting a job without a channel *job-start-nochannel* *** ../vim-7.4.2297/src/version.c 2016-09-01 14:35:19.306759126 +0200 --- src/version.c 2016-09-01 14:50:41.454842822 +0200 *************** *** 765,766 **** --- 765,768 ---- { /* Add new patch number below this line */ + /**/ + 2298, /**/ -- Would you care for a drink? I mean, if it were, like, disabled and you had to look after it? /// 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 ///