To: vim_dev@googlegroups.com Subject: Patch 7.4.1373 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 -- Bram@Moolenaar.net -- 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 ///