# HG changeset patch # User Christian Brabandt # Date 1458504005 -3600 # Node ID 06848fe9c8161b3c2b88eb684481aa4c11b86c16 # Parent 9a56be645421dff26bef257b67b90ff9ac057371 commit https://github.com/vim/vim/commit/03602ec28ed25739e88b2c835adb0662d3720bb2 Author: Bram Moolenaar Date: Sun Mar 20 20:57:45 2016 +0100 patch 7.4.1624 Problem: Can't get info about a channel. Solution: Add ch_info(). diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1,4 +1,4 @@ -*eval.txt* For Vim version 7.4. Last change: 2016 Mar 15 +*eval.txt* For Vim version 7.4. Last change: 2016 Mar 20 VIM REFERENCE MANUAL by Bram Moolenaar @@ -753,7 +753,12 @@ recursively. Ignoring case means case i *E693* *E694* A |Funcref| can only be compared with a |Funcref| and only "equal" and "not -equal" can be used. Case is never ignored. +equal" can be used. Case is never ignored. Whether arguments or a Dictionary +are bound (with a partial) is ignored. This is so that when a function is +made a member of a Dictionary it is still considered to be the same function. +To compare partials to see if they bind the same argument and Dictionary +values use string(): > + echo string(Partial1) == string(Partial2) When using "is" or "isnot" with a |List| or a |Dictionary| this checks if the expressions are referring to the same |List| or |Dictionary| instance. A copy @@ -1822,6 +1827,7 @@ ch_evalraw( {handle}, {string} [, {optio 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} @@ -1832,7 +1838,7 @@ ch_sendexpr( {handle}, {expr} [, {option 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 {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} @@ -2759,6 +2765,32 @@ ch_getjob({channel}) *ch_getjob()* {only available when compiled with the |+channel| and |+job| features} +ch_info({handle}) *ch_info()* + Returns a Dictionary with information about {handle}. The + items are: + "id" number of the channel + "status" "open" (any part is open) or "closed" + When opened with ch_open(): + "hostname" the hostname of the address + "port" the port of the address + "sock_status" "open" or "closed" + "sock_mode" "NL", "RAW", "JSON" or "JS" + "sock_io" "socket" + "sock_timeout" timeout in msec + When opened with job_start(): + "out_status" "open" or "closed" + "out_mode" "NL", "RAW", "JSON" or "JS" + "out_io" "null", "pipe", "file" or "buffer" + "out_timeout" timeout in msec + "err_status" "open" or "closed" + "err_mode" "NL", "RAW", "JSON" or "JS" + "err_io" "out", "null", "pipe", "file" or "buffer" + "err_timeout" timeout in msec + "in_status" "open" or "closed" + "in_mode" "NL", "RAW", "JSON" or "JS" + "in_io" "null", "pipe", "file" or "buffer" + "in_timeout" timeout in msec + ch_log({msg} [, {handle}]) *ch_log()* Write {msg} in the channel log file, if it was opened with |ch_logfile()|. @@ -3594,6 +3626,18 @@ function({name} [, {arglist}] [, {dict}] < Invokes the function as with: > call Callback('one', 'two', 'name') +< The function() call can be nested to add more arguments to the + Funcref. The extra arguments are appended to the list of + arguments. Example: > + func Callback(arg1, arg2, name) + ... + let Func = function('Callback', ['one']) + let Func2 = function(Func, ['two']) + ... + call Func2('name') +< Invokes the function as with: > + call Callback('one', 'two', 'name') + < The Dictionary is only useful when calling a "dict" function. In that case the {dict} is passed in as "self". Example: > function Callback() dict @@ -7050,6 +7094,10 @@ timer_start({time}, {callback} [, {optio intervals. {only available when compiled with the |+timers| feature} +timer_stop({timer}) *timer_stop()* + Stop a timer. {timer} is an ID returned by timer_start(). + The timer callback will no longer be invoked. + tolower({expr}) *tolower()* The result is a copy of the String given, with all uppercase characters turned into lowercase (just like applying |gu| to diff --git a/src/channel.c b/src/channel.c --- a/src/channel.c +++ b/src/channel.c @@ -838,6 +838,8 @@ channel_open( channel->CH_SOCK_FD = (sock_T)sd; channel->ch_nb_close_cb = nb_close_cb; + channel->ch_hostname = (char *)vim_strsave((char_u *)hostname); + channel->ch_port = port_in; #ifdef FEAT_GUI channel_gui_register_one(channel, PART_SOCK); @@ -1138,6 +1140,10 @@ channel_set_options(channel_T *channel, ch_logs(channel, "writing err to buffer '%s'", (char *)channel->ch_part[PART_ERR].ch_buffer->b_ffname); } + + channel->ch_part[PART_OUT].ch_io = opt->jo_io[PART_OUT]; + channel->ch_part[PART_ERR].ch_io = opt->jo_io[PART_ERR]; + channel->ch_part[PART_IN].ch_io = opt->jo_io[PART_IN]; } /* @@ -2088,6 +2094,69 @@ channel_status(channel_T *channel) return "closed"; } + static void +channel_part_info(channel_T *channel, dict_T *dict, char *name, int part) +{ + chanpart_T *chanpart = &channel->ch_part[part]; + char namebuf[20]; + int tail; + char *s; + + STRCPY(namebuf, name); + STRCAT(namebuf, "_"); + tail = STRLEN(namebuf); + + STRCPY(namebuf + tail, "status"); + dict_add_nr_str(dict, namebuf, 0, + (char_u *)(chanpart->ch_fd == INVALID_FD ? "closed" : "open")); + + STRCPY(namebuf + tail, "mode"); + switch (chanpart->ch_mode) + { + case MODE_NL: s = "NL"; break; + case MODE_RAW: s = "RAW"; break; + case MODE_JSON: s = "JSON"; break; + case MODE_JS: s = "JS"; break; + } + dict_add_nr_str(dict, namebuf, 0, (char_u *)s); + + STRCPY(namebuf + tail, "io"); + if (part == PART_SOCK) + s = "socket"; + else switch (chanpart->ch_io) + { + case JIO_NULL: s = "null"; break; + case JIO_PIPE: s = "pipe"; break; + case JIO_FILE: s = "file"; break; + case JIO_BUFFER: s = "buffer"; break; + case JIO_OUT: s = "out"; break; + } + dict_add_nr_str(dict, namebuf, 0, (char_u *)s); + + STRCPY(namebuf + tail, "timeout"); + dict_add_nr_str(dict, namebuf, chanpart->ch_timeout, NULL); +} + + void +channel_info(channel_T *channel, dict_T *dict) +{ + dict_add_nr_str(dict, "id", channel->ch_id, NULL); + dict_add_nr_str(dict, "status", 0, (char_u *)channel_status(channel)); + + if (channel->ch_hostname != NULL) + { + dict_add_nr_str(dict, "hostname", 0, (char_u *)channel->ch_hostname); + dict_add_nr_str(dict, "port", channel->ch_port, NULL); + channel_part_info(channel, dict, "sock", PART_SOCK); + } + else + { + channel_part_info(channel, dict, "out", PART_OUT); + channel_part_info(channel, dict, "err", PART_ERR); + channel_part_info(channel, dict, "in", PART_IN); + } +} + /* * Close channel "channel". * Trigger the close callback if "invoke_close_cb" is TRUE. @@ -2195,6 +2264,8 @@ channel_clear_one(channel_T *channel, in channel_clear(channel_T *channel) { ch_log(channel, "Clearing channel"); + vim_free(channel->ch_hostname); + channel->ch_hostname = NULL; channel_clear_one(channel, PART_SOCK); channel_clear_one(channel, PART_OUT); channel_clear_one(channel, PART_ERR); diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -501,6 +501,7 @@ static void f_ch_evalexpr(typval_T *argv static void f_ch_evalraw(typval_T *argvars, typval_T *rettv); static void f_ch_getbufnr(typval_T *argvars, typval_T *rettv); static void f_ch_getjob(typval_T *argvars, typval_T *rettv); +static void f_ch_info(typval_T *argvars, typval_T *rettv); static void f_ch_log(typval_T *argvars, typval_T *rettv); static void f_ch_logfile(typval_T *argvars, typval_T *rettv); static void f_ch_open(typval_T *argvars, typval_T *rettv); @@ -8141,6 +8142,7 @@ static struct fst {"ch_evalraw", 2, 3, f_ch_evalraw}, {"ch_getbufnr", 2, 2, f_ch_getbufnr}, {"ch_getjob", 1, 1, f_ch_getjob}, + {"ch_info", 1, 1, f_ch_info}, {"ch_log", 1, 2, f_ch_log}, {"ch_logfile", 1, 2, f_ch_logfile}, {"ch_open", 1, 2, f_ch_open}, @@ -10029,6 +10031,18 @@ f_ch_getjob(typval_T *argvars, typval_T } /* + * "ch_info()" function + */ + static void +f_ch_info(typval_T *argvars, typval_T *rettv UNUSED) +{ + channel_T *channel = get_channel_arg(&argvars[0], TRUE); + + if (channel != NULL && rettv_dict_alloc(rettv) != FAIL) + channel_info(channel, rettv->vval.v_dict); +} + +/* * "ch_log()" function */ static void diff --git a/src/proto/channel.pro b/src/proto/channel.pro --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -20,6 +20,7 @@ int channel_collapse(channel_T *channel, int channel_can_write_to(channel_T *channel); int channel_is_open(channel_T *channel); 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); char_u *channel_peek(channel_T *channel, int part); void channel_clear(channel_T *channel); 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 @@ -120,9 +120,16 @@ func s:communicate(port) return endif if has('job') - " check that no job is handled correctly + " check that getjob without a job is handled correctly call assert_equal('no process', string(ch_getjob(handle))) endif + let dict = ch_info(handle) + call assert_true(dict.id != 0) + call assert_equal('open', dict.status) + call assert_equal(a:port, string(dict.port)) + call assert_equal('open', dict.sock_status) + call assert_equal('socket', dict.sock_io) + " Simple string request and reply. call assert_equal('got it', ch_evalexpr(handle, 'hello!')) 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 */ /**/ + 1624, +/**/ 1623, /**/ 1622,