diff src/eval.c @ 7788:192ae655ac91 v7.4.1191

commit https://github.com/vim/vim/commit/3b5f929b18492fec291d1ec95a91f54e5912c03b Author: Bram Moolenaar <Bram@vim.org> Date: Thu Jan 28 22:37:01 2016 +0100 patch 7.4.1191 Problem: The channel feature isn't working yet. Solution: Add the connect(), disconnect(), sendexpr() and sendraw() functions. Add initial documentation. Add a demo server.
author Christian Brabandt <cb@256bit.org>
date Thu, 28 Jan 2016 22:45:04 +0100
parents 3d8e4e0d7127
children 83861277e6a3
line wrap: on
line diff
--- a/src/eval.c
+++ b/src/eval.c
@@ -458,7 +458,6 @@ static int get_env_tv(char_u **arg, typv
 static int find_internal_func(char_u *name);
 static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload);
 static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict);
-static int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict);
 static void emsg_funcname(char *ermsg, char_u *name);
 static int non_zero_arg(typval_T *argvars);
 
@@ -516,6 +515,9 @@ static void f_copy(typval_T *argvars, ty
 static void f_cos(typval_T *argvars, typval_T *rettv);
 static void f_cosh(typval_T *argvars, typval_T *rettv);
 #endif
+#ifdef FEAT_CHANNEL
+static void f_connect(typval_T *argvars, typval_T *rettv);
+#endif
 static void f_count(typval_T *argvars, typval_T *rettv);
 static void f_cscope_connection(typval_T *argvars, typval_T *rettv);
 static void f_cursor(typval_T *argsvars, typval_T *rettv);
@@ -524,6 +526,9 @@ static void f_delete(typval_T *argvars, 
 static void f_did_filetype(typval_T *argvars, typval_T *rettv);
 static void f_diff_filler(typval_T *argvars, typval_T *rettv);
 static void f_diff_hlID(typval_T *argvars, typval_T *rettv);
+#ifdef FEAT_CHANNEL
+static void f_disconnect(typval_T *argvars, typval_T *rettv);
+#endif
 static void f_empty(typval_T *argvars, typval_T *rettv);
 static void f_escape(typval_T *argvars, typval_T *rettv);
 static void f_eval(typval_T *argvars, typval_T *rettv);
@@ -698,6 +703,10 @@ static void f_searchdecl(typval_T *argva
 static void f_searchpair(typval_T *argvars, typval_T *rettv);
 static void f_searchpairpos(typval_T *argvars, typval_T *rettv);
 static void f_searchpos(typval_T *argvars, typval_T *rettv);
+#ifdef FEAT_CHANNEL
+static void f_sendexpr(typval_T *argvars, typval_T *rettv);
+static void f_sendraw(typval_T *argvars, typval_T *rettv);
+#endif
 static void f_server2client(typval_T *argvars, typval_T *rettv);
 static void f_serverlist(typval_T *argvars, typval_T *rettv);
 static void f_setbufvar(typval_T *argvars, typval_T *rettv);
@@ -8170,6 +8179,9 @@ static struct fst
     {"complete_check",	0, 0, f_complete_check},
 #endif
     {"confirm",		1, 4, f_confirm},
+#ifdef FEAT_CHANNEL
+    {"connect",		2, 3, f_connect},
+#endif
     {"copy",		1, 1, f_copy},
 #ifdef FEAT_FLOAT
     {"cos",		1, 1, f_cos},
@@ -8183,6 +8195,9 @@ static struct fst
     {"did_filetype",	0, 0, f_did_filetype},
     {"diff_filler",	1, 1, f_diff_filler},
     {"diff_hlID",	2, 2, f_diff_hlID},
+#ifdef FEAT_CHANNEL
+    {"disconnect",	1, 1, f_disconnect},
+#endif
     {"empty",		1, 1, f_empty},
     {"escape",		2, 2, f_escape},
     {"eval",		1, 1, f_eval},
@@ -8361,6 +8376,10 @@ static struct fst
     {"searchpair",	3, 7, f_searchpair},
     {"searchpairpos",	3, 7, f_searchpairpos},
     {"searchpos",	1, 4, f_searchpos},
+#ifdef FEAT_CHANNEL
+    {"sendexpr",	2, 3, f_sendexpr},
+    {"sendraw",		2, 3, f_sendraw},
+#endif
     {"server2client",	2, 2, f_server2client},
     {"serverlist",	0, 0, f_serverlist},
     {"setbufvar",	3, 3, f_setbufvar},
@@ -8674,7 +8693,7 @@ get_func_tv(name, len, rettv, arg, first
  * Return FAIL when the function can't be called,  OK otherwise.
  * Also returns OK when an error was encountered while executing the function.
  */
-    static int
+    int
 call_func(funcname, len, rettv, argcount, argvars, firstline, lastline,
 						doesrange, evaluate, selfdict)
     char_u	*funcname;	/* name of the function */
@@ -10293,6 +10312,83 @@ f_count(argvars, rettv)
     rettv->vval.v_number = n;
 }
 
+#ifdef FEAT_CHANNEL
+/*
+ * Get a callback from "arg".  It can be a Funcref or a function name.
+ * When "arg" is zero return an empty string.
+ * Return NULL for an invalid argument.
+ */
+    static char_u *
+get_callback(typval_T *arg)
+{
+    if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
+	return arg->vval.v_string;
+    if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
+	return (char_u *)"";
+    EMSG(_("E999: Invalid callback argument"));
+    return NULL;
+}
+
+/*
+ * "connect()" function
+ */
+    static void
+f_connect(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    char_u	*address;
+    char_u	*mode;
+    char_u	*callback = NULL;
+    char_u	buf1[NUMBUFLEN];
+    char_u	*p;
+    int		port;
+    int		json_mode = FALSE;
+
+    address = get_tv_string(&argvars[0]);
+    mode = get_tv_string_buf(&argvars[1], buf1);
+    if (argvars[2].v_type != VAR_UNKNOWN)
+    {
+	callback = get_callback(&argvars[2]);
+	if (callback == NULL)
+	    return;
+    }
+
+    /* parse address */
+    p = vim_strchr(address, ':');
+    if (p == NULL)
+    {
+	EMSG2(_(e_invarg2), address);
+	return;
+    }
+    *p++ = NUL;
+    port = atoi((char *)p);
+    if (*address == NUL || port <= 0)
+    {
+	p[-1] = ':';
+	EMSG2(_(e_invarg2), address);
+	return;
+    }
+
+    /* parse mode */
+    if (STRCMP(mode, "json") == 0)
+	json_mode = TRUE;
+    else if (STRCMP(mode, "raw") != 0)
+    {
+	EMSG2(_(e_invarg2), mode);
+	return;
+    }
+
+    rettv->vval.v_number = channel_open((char *)address, port, NULL);
+    if (rettv->vval.v_number >= 0)
+    {
+	channel_set_json_mode(rettv->vval.v_number, json_mode);
+	if (callback != NULL && *callback != NUL)
+	    channel_set_callback(rettv->vval.v_number, callback);
+    }
+}
+#endif
+
 /*
  * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
  *
@@ -10545,6 +10641,46 @@ f_diff_hlID(argvars, rettv)
 #endif
 }
 
+#ifdef FEAT_CHANNEL
+/*
+ * Get the channel index from the handle argument.
+ * Returns -1 if the handle is invalid or the channel is closed.
+ */
+    static int
+get_channel_arg(typval_T *tv)
+{
+    int ch_idx;
+
+    if (tv->v_type != VAR_NUMBER)
+    {
+	EMSG2(_(e_invarg2), get_tv_string(tv));
+	return -1;
+    }
+    ch_idx = tv->vval.v_number;
+
+    if (!channel_is_open(ch_idx))
+    {
+	EMSGN(_("E999: not an open channel"), ch_idx);
+	return -1;
+    }
+    return ch_idx;
+}
+
+/*
+ * "disconnect()" function
+ */
+    static void
+f_disconnect(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv UNUSED;
+{
+    int ch_idx = get_channel_arg(&argvars[0]);
+
+    if (ch_idx >= 0)
+	channel_close(ch_idx);
+}
+#endif
+
 /*
  * "empty({expr})" function
  */
@@ -17378,6 +17514,109 @@ f_searchpos(argvars, rettv)
 	list_append_number(rettv->vval.v_list, (varnumber_T)n);
 }
 
+#ifdef FEAT_CHANNEL
+/*
+ * common for "sendexpr()" and "sendraw()"
+ * Returns the channel index if the caller should read the response.
+ * Otherwise returns -1.
+ */
+    static int
+send_common(typval_T *argvars, char_u *text, char *fun)
+{
+    int		ch_idx;
+    char_u	*callback = NULL;
+
+    ch_idx = get_channel_arg(&argvars[0]);
+    if (ch_idx < 0)
+	return -1;
+
+    if (argvars[2].v_type != VAR_UNKNOWN)
+    {
+	callback = get_callback(&argvars[2]);
+	if (callback == NULL)
+	    return -1;
+    }
+    /* Set the callback or clear it. An empty callback means no callback and
+     * not reading the response. */
+    channel_set_req_callback(ch_idx,
+	    callback != NULL && *callback == NUL ? NULL : callback);
+    if (callback == NULL)
+	channel_will_block(ch_idx);
+
+    if (channel_send(ch_idx, text, fun) == OK && callback == NULL)
+	return ch_idx;
+    return -1;
+}
+
+/*
+ * "sendexpr()" function
+ */
+    static void
+f_sendexpr(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    char_u	*text;
+    char_u	*resp;
+    typval_T	nrtv;
+    typval_T	listtv;
+    int		ch_idx;
+
+    /* return an empty string by default */
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = NULL;
+
+    nrtv.v_type = VAR_NUMBER;
+    nrtv.vval.v_number = channel_get_id();
+    if (rettv_list_alloc(&listtv) == FAIL)
+	return;
+    if (list_append_tv(listtv.vval.v_list, &nrtv) == FAIL
+	    || list_append_tv(listtv.vval.v_list, &argvars[1]) == FAIL)
+    {
+	list_unref(listtv.vval.v_list);
+	return;
+    }
+
+    text = json_encode(&listtv);
+    list_unref(listtv.vval.v_list);
+
+    ch_idx = send_common(argvars, text, "sendexpr");
+    if (ch_idx >= 0)
+    {
+	/* TODO: read until the whole JSON message is received */
+	/* TODO: only use the message with the right message ID */
+	resp = channel_read_block(ch_idx);
+	if (resp != NULL)
+	{
+	    channel_decode_json(resp, rettv);
+	    vim_free(resp);
+	}
+    }
+}
+
+/*
+ * "sendraw()" function
+ */
+    static void
+f_sendraw(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    char_u	buf[NUMBUFLEN];
+    char_u	*text;
+    int		ch_idx;
+
+    /* return an empty string by default */
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = NULL;
+
+    text = get_tv_string_buf(&argvars[1], buf);
+    ch_idx = send_common(argvars, text, "sendraw");
+    if (ch_idx >= 0)
+	rettv->vval.v_string = channel_read_block(ch_idx);
+}
+#endif
+
 
     static void
 f_server2client(argvars, rettv)