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 -- [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.