changeset 8669:06848fe9c816 v7.4.1624

commit https://github.com/vim/vim/commit/03602ec28ed25739e88b2c835adb0662d3720bb2 Author: Bram Moolenaar <Bram@vim.org> 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().
author Christian Brabandt <cb@256bit.org>
date Sun, 20 Mar 2016 21:00:05 +0100
parents 9a56be645421
children 251ab6b09b05
files runtime/doc/eval.txt src/channel.c src/eval.c src/proto/channel.pro src/testdir/test_channel.vim src/version.c
diffstat 6 files changed, 147 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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);
--- 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
--- 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);
--- 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!'))
 
--- 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,