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.

Raspunde prin e-mail lui