To: vim_dev@googlegroups.com Subject: Patch 7.4.1229 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.1229 Problem: "eval" and "expr" channel commands don't work yet. Solution: Implement them. Update the error numbers. Also add "redraw". Files: src/channel.c, src/eval.c, src/json.c, src/ex_docmd.c, src/proto/channel.pro, src/proto/json.pro, src/proto/ex_docmd.pro, runtime/doc/channel.txt *** ../vim-7.4.1228/src/channel.c 2016-01-30 23:20:28.523141073 +0100 --- src/channel.c 2016-01-31 20:15:26.863506973 +0100 *************** *** 293,306 **** if (idx < 0) { CHERROR("All channels are in use\n", ""); ! EMSG(_("E999: All channels are in use")); return -1; } if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { CHERROR("error in socket() in channel_open()\n", ""); ! PERROR("E999: socket() in channel_open()"); return -1; } --- 293,306 ---- if (idx < 0) { CHERROR("All channels are in use\n", ""); ! EMSG(_("E897: All channels are in use")); return -1; } if ((sd = (sock_T)socket(AF_INET, SOCK_STREAM, 0)) == (sock_T)-1) { CHERROR("error in socket() in channel_open()\n", ""); ! PERROR("E898: socket() in channel_open()"); return -1; } *************** *** 312,318 **** if ((host = gethostbyname(hostname)) == NULL) { CHERROR("error in gethostbyname() in channel_open()\n", ""); ! PERROR("E999: gethostbyname() in channel_open()"); sock_close(sd); return -1; } --- 312,318 ---- if ((host = gethostbyname(hostname)) == NULL) { CHERROR("error in gethostbyname() in channel_open()\n", ""); ! PERROR("E901: gethostbyname() in channel_open()"); sock_close(sd); return -1; } *************** *** 330,336 **** { SOCK_ERRNO; CHERROR("socket() retry in channel_open()\n", ""); ! PERROR("E999: socket() retry in channel_open()"); return -1; } if (connect(sd, (struct sockaddr *)&server, sizeof(server))) --- 330,336 ---- { SOCK_ERRNO; CHERROR("socket() retry in channel_open()\n", ""); ! PERROR("E900: socket() retry in channel_open()"); return -1; } if (connect(sd, (struct sockaddr *)&server, sizeof(server))) *************** *** 362,368 **** { /* Get here when the server can't be found. */ CHERROR("Cannot connect to port after retry\n", ""); ! PERROR(_("E999: Cannot connect to port after retry2")); sock_close(sd); return -1; } --- 362,368 ---- { /* Get here when the server can't be found. */ CHERROR("Cannot connect to port after retry\n", ""); ! PERROR(_("E899: Cannot connect to port after retry2")); sock_close(sd); return -1; } *************** *** 371,377 **** else { CHERROR("Cannot connect to port\n", ""); ! PERROR(_("E999: Cannot connect to port")); sock_close(sd); return -1; } --- 371,377 ---- else { CHERROR("Cannot connect to port\n", ""); ! PERROR(_("E902: Cannot connect to port")); sock_close(sd); return -1; } *************** *** 418,430 **** } /* ! * Decode JSON "msg", which must have the form "[expr1, expr2]". * Put "expr1" in "tv1". * Put "expr2" in "tv2". * Return OK or FAIL. */ int ! channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2) { js_read_T reader; typval_T listtv; --- 418,432 ---- } /* ! * Decode JSON "msg", which must have the form "[expr1, expr2, expr3]". * Put "expr1" in "tv1". * Put "expr2" in "tv2". + * Put "expr3" in "tv3". If "tv3" is NULL there is no "expr3". + * * Return OK or FAIL. */ int ! channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2, typval_T *tv3) { js_read_T reader; typval_T listtv; *************** *** 434,449 **** reader.js_used = 0; json_decode(&reader, &listtv); ! if (listtv.v_type == VAR_LIST && listtv.vval.v_list->lv_len == 2) { ! /* Move the item from the list and then change the type to avoid the ! * item being freed. */ ! *tv1 = listtv.vval.v_list->lv_first->li_tv; ! listtv.vval.v_list->lv_first->li_tv.v_type = VAR_NUMBER; ! *tv2 = listtv.vval.v_list->lv_last->li_tv; ! listtv.vval.v_list->lv_last->li_tv.v_type = VAR_NUMBER; ! list_unref(listtv.vval.v_list); ! return OK; } /* give error message? */ --- 436,466 ---- reader.js_used = 0; json_decode(&reader, &listtv); ! if (listtv.v_type == VAR_LIST) { ! list_T *list = listtv.vval.v_list; ! ! if (list->lv_len == 2 || (tv3 != NULL && list->lv_len == 3)) ! { ! /* Move the item from the list and then change the type to avoid the ! * item being freed. */ ! *tv1 = list->lv_first->li_tv; ! list->lv_first->li_tv.v_type = VAR_NUMBER; ! *tv2 = list->lv_first->li_next->li_tv; ! list->lv_first->li_next->li_tv.v_type = VAR_NUMBER; ! if (tv3 != NULL) ! { ! if (list->lv_len == 3) ! { ! *tv3 = list->lv_last->li_tv; ! list->lv_last->li_tv.v_type = VAR_NUMBER; ! } ! else ! tv3->v_type = VAR_UNKNOWN; ! } ! list_unref(list); ! return OK; ! } } /* give error message? */ *************** *** 472,515 **** out_flush(); } static void ! channel_exe_cmd(char_u *cmd, typval_T *arg) { if (STRCMP(cmd, "ex") == 0) { ! if (arg->v_type == VAR_STRING) ! do_cmdline_cmd(arg->vval.v_string); ! else if (p_verbose > 2) ! EMSG("E999: received ex command with non-string argument"); } else if (STRCMP(cmd, "normal") == 0) { ! if (arg->v_type == VAR_STRING) ! { ! exarg_T ea; ! ea.arg = arg->vval.v_string; ! ea.addr_count = 0; ! ea.forceit = TRUE; /* no mapping */ ! ex_normal(&ea); ! ! update_screen(0); ! showruler(FALSE); ! setcursor(); ! out_flush(); #ifdef FEAT_GUI ! if (gui.in_use) { ! gui_update_cursor(FALSE, FALSE); ! gui_mch_flush(); } ! #endif } - else if (p_verbose > 2) - EMSG("E999: received normal command with non-string argument"); } else if (p_verbose > 2) ! EMSG2("E999: received unknown command: %s", cmd); } /* --- 489,574 ---- out_flush(); } + /* + * Execute a command received over channel "idx". + * "cmd" is the command string, "arg2" the second argument. + * "arg3" is the third argument, NULL if missing. + */ static void ! channel_exe_cmd(int idx, 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 (STRCMP(cmd, "ex") == 0) { ! do_cmdline_cmd(arg); } else if (STRCMP(cmd, "normal") == 0) { ! exarg_T ea; ! ea.arg = arg; ! ea.addr_count = 0; ! ea.forceit = TRUE; /* no mapping */ ! ex_normal(&ea); ! } ! else if (STRCMP(cmd, "redraw") == 0) ! { ! exarg_T ea; ! ! ea.forceit = *arg != NUL; ! ex_redraw(&ea); ! showruler(FALSE); ! setcursor(); ! out_flush(); #ifdef FEAT_GUI ! if (gui.in_use) ! { ! gui_update_cursor(FALSE, FALSE); ! gui_mch_flush(); ! } ! #endif ! } ! else if (STRCMP(cmd, "expr") == 0 || STRCMP(cmd, "eval") == 0) ! { ! int is_eval = cmd[1] == 'v'; ! ! if (is_eval && arg3->v_type != VAR_NUMBER) ! { ! if (p_verbose > 2) ! EMSG("E904: third argument for eval must be a number"); ! } ! else ! { ! typval_T *tv = eval_expr(arg, NULL); ! typval_T err_tv; ! char_u *json; ! ! if (is_eval) { ! if (tv == NULL) ! { ! 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); ! channel_send(idx, json, "eval"); ! vim_free(json); } ! free_tv(tv); } } else if (p_verbose > 2) ! EMSG2("E905: received unknown command: %s", cmd); } /* *************** *** 521,526 **** --- 580,586 ---- char_u *msg; typval_T typetv; typval_T argv[3]; + typval_T arg3; char_u *cmd = NULL; int seq_nr = -1; int ret = OK; *************** *** 537,545 **** if (channels[idx].ch_json_mode) { ! ret = channel_decode_json(msg, &typetv, &argv[1]); if (ret == OK) { if (typetv.v_type == VAR_STRING) cmd = typetv.vval.v_string; else if (typetv.v_type == VAR_NUMBER) --- 597,606 ---- if (channels[idx].ch_json_mode) { ! ret = channel_decode_json(msg, &typetv, &argv[1], &arg3); if (ret == OK) { + /* TODO: error if arg3 is set when it shouldn't? */ if (typetv.v_type == VAR_STRING) cmd = typetv.vval.v_string; else if (typetv.v_type == VAR_NUMBER) *************** *** 556,562 **** { if (cmd != NULL) { ! channel_exe_cmd(cmd, &argv[1]); } else if (channels[idx].ch_req_callback != NULL && seq_nr != 0) { --- 617,623 ---- { if (cmd != NULL) { ! channel_exe_cmd(idx, cmd, &argv[1], &arg3); } else if (channels[idx].ch_req_callback != NULL && seq_nr != 0) { *************** *** 576,581 **** --- 637,643 ---- { clear_tv(&typetv); clear_tv(&argv[1]); + clear_tv(&arg3); } } *************** *** 874,880 **** { /* Todo: which channel? */ CHERROR("%s(): cannot from channel\n", "channel_read"); ! PERROR(_("E999: read from channel")); } } --- 936,942 ---- { /* Todo: which channel? */ CHERROR("%s(): cannot from channel\n", "channel_read"); ! PERROR(_("E896: read from channel")); } } *** ../vim-7.4.1228/src/eval.c 2016-01-31 18:45:20.996084796 +0100 --- src/eval.c 2016-01-31 19:36:10.932096638 +0100 *************** *** 16897,16904 **** { char_u *text; char_u *resp; - typval_T nrtv; - typval_T listtv; typval_T typetv; int ch_idx; --- 16897,16902 ---- *************** *** 16906,16924 **** 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) --- 16904,16912 ---- rettv->v_type = VAR_STRING; rettv->vval.v_string = NULL; ! text = json_encode_nr_expr(channel_get_id(), &argvars[1]); ! if (text == NULL) return; ch_idx = send_common(argvars, text, "sendexpr"); if (ch_idx >= 0) *************** *** 16929,16935 **** resp = channel_read_block(ch_idx); if (resp != NULL) { ! channel_decode_json(resp, &typetv, rettv); vim_free(resp); } } --- 16917,16923 ---- resp = channel_read_block(ch_idx); if (resp != NULL) { ! channel_decode_json(resp, &typetv, rettv, NULL); vim_free(resp); } } *** ../vim-7.4.1228/src/json.c 2016-01-28 22:46:52.017343116 +0100 --- src/json.c 2016-01-31 19:32:13.738584392 +0100 *************** *** 33,38 **** --- 33,65 ---- return ga.ga_data; } + /* + * Encode ["nr", "val"] into a JSON format string. + * Returns NULL when out of memory. + */ + char_u * + json_encode_nr_expr(int nr, typval_T *val) + { + typval_T listtv; + typval_T nrtv; + char_u *text; + + nrtv.v_type = VAR_NUMBER; + nrtv.vval.v_number = nr; + if (rettv_list_alloc(&listtv) == FAIL) + return NULL; + if (list_append_tv(listtv.vval.v_list, &nrtv) == FAIL + || list_append_tv(listtv.vval.v_list, val) == FAIL) + { + list_unref(listtv.vval.v_list); + return NULL; + } + + text = json_encode(&listtv); + list_unref(listtv.vval.v_list); + return text; + } + static void write_string(garray_T *gap, char_u *str) { *** ../vim-7.4.1228/src/ex_docmd.c 2016-01-31 14:55:35.227538473 +0100 --- src/ex_docmd.c 2016-01-31 20:15:14.095639725 +0100 *************** *** 335,341 **** static void ex_redo(exarg_T *eap); static void ex_later(exarg_T *eap); static void ex_redir(exarg_T *eap); - static void ex_redraw(exarg_T *eap); static void ex_redrawstatus(exarg_T *eap); static void close_redir(void); static void ex_mkrc(exarg_T *eap); --- 335,340 ---- *************** *** 9466,9472 **** /* * ":redraw": force redraw */ ! static void ex_redraw(exarg_T *eap) { int r = RedrawingDisabled; --- 9465,9471 ---- /* * ":redraw": force redraw */ ! void ex_redraw(exarg_T *eap) { int r = RedrawingDisabled; *** ../vim-7.4.1228/src/proto/channel.pro 2016-01-30 23:20:28.523141073 +0100 --- src/proto/channel.pro 2016-01-31 19:36:46.331725396 +0100 *************** *** 4,10 **** void channel_set_json_mode(int idx, int json_mode); void channel_set_callback(int idx, char_u *callback); void channel_set_req_callback(int idx, char_u *callback); ! int channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2); int channel_is_open(int idx); void channel_close(int idx); int channel_save(int idx, char_u *buf, int len); --- 4,10 ---- void channel_set_json_mode(int idx, int json_mode); void channel_set_callback(int idx, char_u *callback); void channel_set_req_callback(int idx, char_u *callback); ! int channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2, typval_T *tv3); int channel_is_open(int idx); void channel_close(int idx); int channel_save(int idx, char_u *buf, int len); *** ../vim-7.4.1228/src/proto/json.pro 2016-01-24 16:49:06.227712998 +0100 --- src/proto/json.pro 2016-01-31 19:35:59.988211410 +0100 *************** *** 1,4 **** --- 1,5 ---- /* json.c */ char_u *json_encode(typval_T *val); + char_u *json_encode_nr_expr(int nr, typval_T *val); void json_decode(js_read_T *reader, typval_T *res); /* vim: set ft=c : */ *** ../vim-7.4.1228/src/proto/ex_docmd.pro 2016-01-30 23:20:28.531140989 +0100 --- src/proto/ex_docmd.pro 2016-01-31 20:15:18.503593894 +0100 *************** *** 46,51 **** --- 46,52 ---- void ex_cd(exarg_T *eap); void do_sleep(long msec); void ex_may_print(exarg_T *eap); + void ex_redraw(exarg_T *eap); int vim_mkdir_emsg(char_u *name, int prot); FILE *open_exfile(char_u *fname, int forceit, char *mode); void update_topline_cursor(void); *** ../vim-7.4.1228/runtime/doc/channel.txt 2016-01-28 22:36:15.056065002 +0100 --- runtime/doc/channel.txt 2016-01-31 20:07:19.752587340 +0100 *************** *** 1,4 **** ! *channel.txt* For Vim version 7.4. Last change: 2016 Jan 28 VIM REFERENCE MANUAL by Bram Moolenaar --- 1,4 ---- ! *channel.txt* For Vim version 7.4. Last change: 2016 Jan 31 VIM REFERENCE MANUAL by Bram Moolenaar *************** *** 48,57 **** The number will increase every time you send a message. The server can send a command to Vim. Type this on T1 (literally, including ! the quotes): > ! NOT IMPLEMENTED YET ! ["ex","echo 'hi there'"] ! And you should see the message in Vim. To handle asynchronous communication a callback needs to be used: > func MyHandler(handle, msg) --- 48,57 ---- The number will increase every time you send a message. The server can send a command to Vim. Type this on T1 (literally, including ! the quotes): ! ["ex","echo 'hi there'"] ~ ! And you should see the message in Vim. You can move the cursor a word forward: ! ["normal","w"] ~ To handle asynchronous communication a callback needs to be used: > func MyHandler(handle, msg) *************** *** 100,105 **** --- 100,113 ---- Once done with the channel, disconnect it like this: > call disconnect(handle) + Currently up to 10 channels can be in use at the same time. *E897* + + When the channel can't be opened you will get an error message. + *E898* *E899* *E900* *E901* *E902* + + If there is an error reading or writing a channel it will be closed. + *E896* *E630* *E631* + ============================================================================== 3. Using a JSON channel *channel-use* *************** *** 146,181 **** ============================================================================== 4. Vim commands *channel-commands* ! NOT IMPLEMENTED YET With a "json" channel the process can send commands to Vim that will be handled by Vim internally, it does not require a handler for the channel. ! Possible commands are: ["ex", {Ex command}] ["normal", {Normal mode command}] ! ["eval", {number}, {expression}] ["expr", {expression}] With all of these: Be careful what these commands do! You can easily interfere with what the user is doing. To avoid trouble use |mode()| to check that the editor is in the expected state. E.g., to send keys that must be ! inserted as text, not executed as a command: > ! ["ex","if mode() == 'i' | call feedkeys('ClassName') | endif"] The "ex" command is executed as any Ex command. There is no response for ! completion or error. You could use functions in an |autoload| script. ! You can also invoke |feedkeys()| to insert anything. ! The "normal" command is executed like with |:normal|. ! The "eval" command will result in sending back the result of the expression: [{number}, {result}] ! Here {number} is the same as what was in the request. ! The "expr" command is similar, but does not send back any response. Example: ! ["expr","setline('$', ['one', 'two', 'three'])"] ============================================================================== 5. Using a raw channel *channel-raw* --- 154,230 ---- ============================================================================== 4. Vim commands *channel-commands* ! PARTLY IMPLEMENTED: only "ex" and "normal" work With a "json" channel the process can send commands to Vim that will be handled by Vim internally, it does not require a handler for the channel. ! Possible commands are: *E903* *E904* *E905* ! ["redraw" {forced}] ["ex", {Ex command}] ["normal", {Normal mode command}] ! ["eval", {expression}, {number}] ["expr", {expression}] With all of these: Be careful what these commands do! You can easily interfere with what the user is doing. To avoid trouble use |mode()| to check that the editor is in the expected state. E.g., to send keys that must be ! inserted as text, not executed as a command: ! ["ex","if mode() == 'i' | call feedkeys('ClassName') | endif"] ~ ! ! Errors in these commands are normally not reported to avoid them messing up ! the display. If you do want to see them, set the 'verbose' option to 3 or ! higher. ! ! ! Command "redraw" ~ ! ! The other commands do not update the screen, so that you can send a sequence ! of commands without the cursor moving around. You must end with the "redraw" ! command to show any changed text and show the cursor where it belongs. ! ! The argument is normally an empty string: ! ["redraw", ""] ~ ! To first clear the screen pass "force": ! ["redraw", "force"] ~ ! ! ! Command "ex" ~ The "ex" command is executed as any Ex command. There is no response for ! completion or error. You could use functions in an |autoload| script: ! ["ex","call myscript#MyFunc(arg)"] ! You can also use "call |feedkeys()|" to insert any key sequence. ! ! Command "normal" ~ ! ! The "normal" command is executed like with |:normal!|, commands are not ! mapped. Example to open the folds under the cursor: ! ["normal" "zO"] ! ! ! Command "eval" ~ ! ! The "eval" command an be used to get the result of an expression. For ! example, to get the number of lines in the current buffer: ! ["eval","line('$')"] ~ ! ! it will send back the result of the expression: [{number}, {result}] ! Here {number} is the same as what was in the request. Use a negative number ! to avoid confusion with message that Vim sends. ! ! {result} is the result of the evaluation and is JSON encoded. If the ! evaluation fails it is the string "ERROR". ! ! ! Command "expr" ~ ! The "expr" command is similar to "eval", but does not send back any response. Example: ! ["expr","setline('$', ['one', 'two', 'three'])"] ~ ============================================================================== 5. Using a raw channel *channel-raw* *** ../vim-7.4.1228/src/version.c 2016-01-31 18:45:20.996084796 +0100 --- src/version.c 2016-01-31 20:13:31.280708829 +0100 *************** *** 744,745 **** --- 744,747 ---- { /* Add new patch number below this line */ + /**/ + 1229, /**/ -- I AM THANKFUL... ...for the mess to clean after a party because it means I have been surrounded by friends. /// 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 ///