After this patch, sending an eval channel command never gets a return
message.  I don't get an error either.  Has the syntax changed?  The
documentation isn't updated.

On Sat, Feb 20, 2016 at 1:39 PM, Bram Moolenaar <[email protected]> wrote:

>
> Patch 7.4.1373
> Problem:    Calling a Vim function over a channel requires turning the
>             arguments into a string.
> Solution:   Add the "call" command. (Damien)  Also merge "expr" and "eval"
>             into one.
> Files:      src/channel.c, src/testdir/test_channel.py,
>             src/testdir/test_channel.vim
>
>
> *** ../vim-7.4.1372/src/channel.c       2016-02-20 19:56:08.999279558 +0100
> --- src/channel.c       2016-02-20 21:32:38.814632787 +0100
> ***************
> *** 1080,1107 ****
>       return FAIL;
>   }
>
>   /*
>    * Execute a command received over "channel"/"part"
> !  * "cmd" is the command string, "arg2" the second argument.
> !  * "arg3" is the third argument, NULL if missing.
>    */
>       static void
> ! channel_exe_cmd(
> !       channel_T *channel,
> !       int part,
> !       char_u *cmd,
> !       typval_T *arg2,
> !       typval_T *arg3)
>   {
> !     char_u *arg;
>
> !     if (arg2->v_type != VAR_STRING)
>       {
>         if (p_verbose > 2)
> !           EMSG("E903: received ex command with non-string argument");
>         return;
>       }
> !     arg = arg2->vval.v_string;
>       if (arg == NULL)
>         arg = (char_u *)"";
>
> --- 1080,1107 ----
>       return FAIL;
>   }
>
> + #define CH_JSON_MAX_ARGS 4
> +
>   /*
>    * Execute a command received over "channel"/"part"
> !  * "argv[0]" is the command string.
> !  * "argv[1]" etc. have further arguments, type is VAR_UNKNOWN if missing.
>    */
>       static void
> ! channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
>   {
> !     char_u  *cmd = argv[0].vval.v_string;
> !     char_u  *arg;
> !     int           options = channel->ch_part[part].ch_mode == MODE_JS ?
> JSON_JS : 0;
>
> !     if (argv[1].v_type != VAR_STRING)
>       {
> +       ch_error(channel, "received command with non-string argument");
>         if (p_verbose > 2)
> !           EMSG("E903: received command with non-string argument");
>         return;
>       }
> !     arg = argv[1].vval.v_string;
>       if (arg == NULL)
>         arg = (char_u *)"";
>
> ***************
> *** 1135,1165 ****
>         }
>   #endif
>       }
> !     else if (STRCMP(cmd, "expr") == 0 || STRCMP(cmd, "eval") == 0)
>       {
> !       int is_eval = cmd[1] == 'v';
>
> !       if (is_eval && (arg3 == NULL || arg3->v_type != VAR_NUMBER))
>         {
>             if (p_verbose > 2)
> !               EMSG("E904: third argument for eval must be a number");
>         }
>         else
>         {
>             typval_T    *tv;
>             typval_T    err_tv;
>             char_u      *json = NULL;
> -           int         options = channel->ch_part[part].ch_mode == MODE_JS
> -                                                               ? JSON_JS
> : 0;
>
>             /* Don't pollute the display with errors. */
>             ++emsg_skip;
> !           tv = eval_expr(arg, NULL);
> !           if (is_eval)
>             {
>                 if (tv != NULL)
> !                   json = json_encode_nr_expr(arg3->vval.v_number, tv,
> !
> options);
>                 if (tv == NULL || (json != NULL && *json == NUL))
>                 {
>                     /* If evaluation failed or the result can't be encoded
> --- 1135,1180 ----
>         }
>   #endif
>       }
> !     else if (STRCMP(cmd, "expr") == 0 || STRCMP(cmd, "call") == 0)
>       {
> !       int is_call = cmd[0] == 'c';
> !       int id_idx = is_call ? 3 : 2;
>
> !       if (argv[id_idx].v_type != VAR_UNKNOWN
> !                                        && argv[id_idx].v_type !=
> VAR_NUMBER)
> !       {
> !           ch_error(channel, "last argument for expr/call must be a
> number");
> !           if (p_verbose > 2)
> !               EMSG("E904: last argument for expr/call must be a number");
> !       }
> !       else if (is_call && argv[2].v_type != VAR_LIST)
>         {
> +           ch_error(channel, "third argument for call must be a list");
>             if (p_verbose > 2)
> !               EMSG("E904: third argument for call must be a list");
>         }
>         else
>         {
>             typval_T    *tv;
> +           typval_T    res_tv;
>             typval_T    err_tv;
>             char_u      *json = NULL;
>
>             /* Don't pollute the display with errors. */
>             ++emsg_skip;
> !           if (!is_call)
> !               tv = eval_expr(arg, NULL);
> !           else if (func_call(arg, &argv[2], NULL, &res_tv) == OK)
> !               tv = &res_tv;
> !           else
> !               tv = NULL;
> !
> !           if (argv[id_idx].v_type == VAR_NUMBER)
>             {
> +               int id = argv[id_idx].vval.v_number;
> +
>                 if (tv != NULL)
> !                   json = json_encode_nr_expr(id, tv, options);
>                 if (tv == NULL || (json != NULL && *json == NUL))
>                 {
>                     /* If evaluation failed or the result can't be encoded
> ***************
> *** 1169,1190 ****
>                     err_tv.v_type = VAR_STRING;
>                     err_tv.vval.v_string = (char_u *)"ERROR";
>                     tv = &err_tv;
> !                   json = json_encode_nr_expr(arg3->vval.v_number, tv,
> !
> options);
>                 }
>                 if (json != NULL)
>                 {
> !                   channel_send(channel, part, json, "eval");
>                     vim_free(json);
>                 }
>             }
>             --emsg_skip;
> !           if (tv != &err_tv)
>                 free_tv(tv);
>         }
>       }
>       else if (p_verbose > 2)
>         EMSG2("E905: received unknown command: %s", cmd);
>   }
>
>   /*
> --- 1184,1211 ----
>                     err_tv.v_type = VAR_STRING;
>                     err_tv.vval.v_string = (char_u *)"ERROR";
>                     tv = &err_tv;
> !                   json = json_encode_nr_expr(id, tv, options);
>                 }
>                 if (json != NULL)
>                 {
> !                   channel_send(channel,
> !                                part == PART_SOCK ? PART_SOCK : PART_IN,
> !                                json, (char *)cmd);
>                     vim_free(json);
>                 }
>             }
>             --emsg_skip;
> !           if (tv == &res_tv)
> !               clear_tv(tv);
> !           else if (tv != &err_tv)
>                 free_tv(tv);
>         }
>       }
>       else if (p_verbose > 2)
> +     {
> +       ch_errors(channel, "Receved unknown command: %s", (char *)cmd);
>         EMSG2("E905: received unknown command: %s", cmd);
> +     }
>   }
>
>   /*
> ***************
> *** 1196,1204 ****
>   {
>       char_u    *msg = NULL;
>       typval_T  *listtv = NULL;
> !     list_T    *list;
> !     typval_T  *typetv;
> !     typval_T  argv[3];
>       int               seq_nr = -1;
>       ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
>       char_u    *callback = NULL;
> --- 1217,1223 ----
>   {
>       char_u    *msg = NULL;
>       typval_T  *listtv = NULL;
> !     typval_T  argv[CH_JSON_MAX_ARGS];
>       int               seq_nr = -1;
>       ch_mode_T ch_mode = channel->ch_part[part].ch_mode;
>       char_u    *callback = NULL;
> ***************
> *** 1214,1219 ****
> --- 1233,1241 ----
>
>       if (ch_mode == MODE_JSON || ch_mode == MODE_JS)
>       {
> +       listitem_T      *item;
> +       int             argc = 0;
> +
>         /* Get any json message in the queue. */
>         if (channel_get_json(channel, part, -1, &listtv) == FAIL)
>         {
> ***************
> *** 1223,1253 ****
>                 return FALSE;
>         }
>
> !       list = listtv->vval.v_list;
> !       argv[1] = list->lv_first->li_next->li_tv;
> !       typetv = &list->lv_first->li_tv;
> !       if (typetv->v_type == VAR_STRING)
> !       {
> !           typval_T    *arg3 = NULL;
> !           char_u      *cmd = typetv->vval.v_string;
> !
> !           /* ["cmd", arg] or ["cmd", arg, arg] */
> !           if (list->lv_len == 3)
> !               arg3 = &list->lv_last->li_tv;
>             ch_logs(channel, "Executing %s command", (char *)cmd);
> !           channel_exe_cmd(channel, part, cmd, &argv[1], arg3);
>             free_tv(listtv);
>             return TRUE;
>         }
>
> !       if (typetv->v_type != VAR_NUMBER)
>         {
>             ch_error(channel,
>                       "Dropping message with invalid sequence number
> type");
>             free_tv(listtv);
>             return FALSE;
>         }
> !       seq_nr = typetv->vval.v_number;
>       }
>       else if (channel_peek(channel, part) == NULL)
>       {
> --- 1245,1276 ----
>                 return FALSE;
>         }
>
> !       for (item = listtv->vval.v_list->lv_first;
> !                           item != NULL && argc < CH_JSON_MAX_ARGS;
> !                                                   item = item->li_next)
> !           argv[argc++] = item->li_tv;
> !       while (argc < CH_JSON_MAX_ARGS)
> !           argv[argc++].v_type = VAR_UNKNOWN;
> !
> !       if (argv[0].v_type == VAR_STRING)
> !       {
> !           char_u      *cmd = argv[0].vval.v_string;
> !
> !           /* ["cmd", arg] or ["cmd", arg, arg] or ["cmd", arg, arg, arg]
> */
>             ch_logs(channel, "Executing %s command", (char *)cmd);
> !           channel_exe_cmd(channel, part, argv);
>             free_tv(listtv);
>             return TRUE;
>         }
>
> !       if (argv[0].v_type != VAR_NUMBER)
>         {
>             ch_error(channel,
>                       "Dropping message with invalid sequence number
> type");
>             free_tv(listtv);
>             return FALSE;
>         }
> !       seq_nr = argv[0].vval.v_number;
>       }
>       else if (channel_peek(channel, part) == NULL)
>       {
> *** ../vim-7.4.1372/src/testdir/test_channel.py 2016-02-20
> 18:26:43.664053539 +0100
> --- src/testdir/test_channel.py 2016-02-20 20:32:47.068182113 +0100
> ***************
> *** 78,103 ****
>                           response = "ok"
>                       elif decoded[1] == 'eval-works':
>                           # Send an eval request.  We ignore the response.
> !                         cmd = '["eval","\\"foo\\" . 123", -1]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
>                       elif decoded[1] == 'eval-fails':
>                           # Send an eval request that will fail.
> !                         cmd = '["eval","xxx", -2]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
>                       elif decoded[1] == 'eval-error':
>                           # Send an eval request that works but the result
> can't
>                           # be encoded.
> !                         cmd = '["eval","function(\\"tr\\")", -3]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
>                       elif decoded[1] == 'eval-bad':
>                           # Send an eval request missing the third
> argument.
> !                         cmd = '["eval","xxx"]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
> --- 78,103 ----
>                           response = "ok"
>                       elif decoded[1] == 'eval-works':
>                           # Send an eval request.  We ignore the response.
> !                         cmd = '["expr","\\"foo\\" . 123", -1]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
>                       elif decoded[1] == 'eval-fails':
>                           # Send an eval request that will fail.
> !                         cmd = '["expr","xxx", -2]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
>                       elif decoded[1] == 'eval-error':
>                           # Send an eval request that works but the result
> can't
>                           # be encoded.
> !                         cmd = '["expr","function(\\"tr\\")", -3]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
>                       elif decoded[1] == 'eval-bad':
>                           # Send an eval request missing the third
> argument.
> !                         cmd = '["expr","xxx"]'
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
> ***************
> *** 107,112 ****
> --- 107,117 ----
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = "ok"
> +                     elif decoded[1] == 'call-func':
> +                         cmd = '["call","MyFunction",[1,2,3], 0]'
> +                         print("sending: {}".format(cmd))
> +                         self.request.sendall(cmd.encode('utf-8'))
> +                         response = "ok"
>                       elif decoded[1] == 'redraw':
>                           cmd = '["redraw",""]'
>                           print("sending: {}".format(cmd))
> ***************
> *** 135,140 ****
> --- 140,148 ----
>                           print("sending: {}".format(cmd))
>                           self.request.sendall(cmd.encode('utf-8'))
>                           response = ""
> +                     elif decoded[1] == 'wait a bit':
> +                         time.sleep(0.2)
> +                         response = "waited"
>                       elif decoded[1] == '!quit!':
>                           # we're done
>                           self.server.shutdown()
> *** ../vim-7.4.1372/src/testdir/test_channel.vim        2016-02-20
> 19:56:09.011279432 +0100
> --- src/testdir/test_channel.vim        2016-02-20 20:34:14.363266356 +0100
> ***************
> *** 431,433 ****
> --- 431,456 ----
>     " The server will wait half a second before creating the port.
>     call s:run_server('s:open_delay', 'delay')
>   endfunc
> +
> + """""""""
> +
> + function MyFunction(a,b,c)
> +   let s:call_ret = [a:a, a:b, a:c]
> + endfunc
> +
> + function s:test_call(port)
> +   let handle = ch_open('localhost:' . a:port, s:chopt)
> +   if ch_status(handle) == "fail"
> +     call assert_false(1, "Can't open channel")
> +     return
> +   endif
> +
> +   call assert_equal('ok', ch_sendexpr(handle, 'call-func'))
> +   sleep 20m
> +   call assert_equal([1, 2, 3], s:call_ret)
> + endfunc
> +
> + func Test_call()
> +   call ch_log('Test_call()')
> +   call s:run_server('s:test_call')
> + endfunc
> *** ../vim-7.4.1372/src/version.c       2016-02-20 19:56:09.011279432 +0100
> --- src/version.c       2016-02-20 21:37:36.503521505 +0100
> ***************
> *** 749,750 ****
> --- 749,752 ----
>   {   /* Add new patch number below this line */
> + /**/
> +     1373,
>   /**/
>
> --
> Computers are not intelligent.  They only think they are.
>
>  /// Bram Moolenaar -- [email protected] -- 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
> ///
>
> --
> --
> You received this message from the "vim_dev" maillist.
> Do not top-post! Type your reply below the text you are replying to.
> For more information, visit http://www.vim.org/maillist.php
>
> ---
> You received this message because you are subscribed to the Google Groups
> "vim_dev" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to [email protected].
> For more options, visit https://groups.google.com/d/optout.
>



-- 
Christian J. Robinson <[email protected]>

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui