mirror of
https://github.com/vim/vim.git
synced 2025-09-23 03:43:49 -04:00
patch 7.4.1229
Problem: "eval" and "expr" channel commands don't work yet. Solution: Implement them. Update the error numbers. Also add "redraw".
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
*channel.txt* For Vim version 7.4. Last change: 2016 Jan 28
|
||||
*channel.txt* For Vim version 7.4. Last change: 2016 Jan 31
|
||||
|
||||
|
||||
VIM REFERENCE MANUAL by Bram Moolenaar
|
||||
@@ -48,10 +48,10 @@ And the response is:
|
||||
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.
|
||||
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,6 +100,14 @@ When {callback} is empty (zero or an empty string) the handler is removed.
|
||||
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,36 +154,77 @@ The channel will then be inactive.
|
||||
==============================================================================
|
||||
4. Vim commands *channel-commands*
|
||||
|
||||
NOT IMPLEMENTED YET
|
||||
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:
|
||||
Possible commands are: *E903* *E904* *E905*
|
||||
["redraw" {forced}]
|
||||
["ex", {Ex command}]
|
||||
["normal", {Normal mode command}]
|
||||
["eval", {number}, {expression}]
|
||||
["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"]
|
||||
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.
|
||||
You can also invoke |feedkeys()| to insert anything.
|
||||
completion or error. You could use functions in an |autoload| script:
|
||||
["ex","call myscript#MyFunc(arg)"]
|
||||
|
||||
The "normal" command is executed like with |:normal|.
|
||||
You can also use "call |feedkeys()|" to insert any key sequence.
|
||||
|
||||
The "eval" command will result in sending back the result of the expression:
|
||||
|
||||
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.
|
||||
Here {number} is the same as what was in the request. Use a negative number
|
||||
to avoid confusion with message that Vim sends.
|
||||
|
||||
The "expr" command is similar, but does not send back any response.
|
||||
{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'])"]
|
||||
["expr","setline('$', ['one', 'two', 'three'])"] ~
|
||||
|
||||
==============================================================================
|
||||
5. Using a raw channel *channel-raw*
|
||||
|
152
src/channel.c
152
src/channel.c
@@ -293,14 +293,14 @@ channel_open(char *hostname, int port_in, void (*close_cb)(void))
|
||||
if (idx < 0)
|
||||
{
|
||||
CHERROR("All channels are in use\n", "");
|
||||
EMSG(_("E999: All channels are in use"));
|
||||
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("E999: socket() in channel_open()");
|
||||
PERROR("E898: socket() in channel_open()");
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ channel_open(char *hostname, int port_in, void (*close_cb)(void))
|
||||
if ((host = gethostbyname(hostname)) == NULL)
|
||||
{
|
||||
CHERROR("error in gethostbyname() in channel_open()\n", "");
|
||||
PERROR("E999: gethostbyname() in channel_open()");
|
||||
PERROR("E901: gethostbyname() in channel_open()");
|
||||
sock_close(sd);
|
||||
return -1;
|
||||
}
|
||||
@@ -330,7 +330,7 @@ channel_open(char *hostname, int port_in, void (*close_cb)(void))
|
||||
{
|
||||
SOCK_ERRNO;
|
||||
CHERROR("socket() retry in channel_open()\n", "");
|
||||
PERROR("E999: socket() retry in channel_open()");
|
||||
PERROR("E900: socket() retry in channel_open()");
|
||||
return -1;
|
||||
}
|
||||
if (connect(sd, (struct sockaddr *)&server, sizeof(server)))
|
||||
@@ -362,7 +362,7 @@ channel_open(char *hostname, int port_in, void (*close_cb)(void))
|
||||
{
|
||||
/* 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"));
|
||||
PERROR(_("E899: Cannot connect to port after retry2"));
|
||||
sock_close(sd);
|
||||
return -1;
|
||||
}
|
||||
@@ -371,7 +371,7 @@ channel_open(char *hostname, int port_in, void (*close_cb)(void))
|
||||
else
|
||||
{
|
||||
CHERROR("Cannot connect to port\n", "");
|
||||
PERROR(_("E999: Cannot connect to port"));
|
||||
PERROR(_("E902: Cannot connect to port"));
|
||||
sock_close(sd);
|
||||
return -1;
|
||||
}
|
||||
@@ -418,13 +418,15 @@ channel_set_req_callback(int idx, char_u *callback)
|
||||
}
|
||||
|
||||
/*
|
||||
* Decode JSON "msg", which must have the form "[expr1, expr2]".
|
||||
* 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)
|
||||
channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2, typval_T *tv3)
|
||||
{
|
||||
js_read_T reader;
|
||||
typval_T listtv;
|
||||
@@ -434,16 +436,31 @@ channel_decode_json(char_u *msg, typval_T *tv1, typval_T *tv2)
|
||||
reader.js_used = 0;
|
||||
json_decode(&reader, &listtv);
|
||||
|
||||
if (listtv.v_type == VAR_LIST && listtv.vval.v_list->lv_len == 2)
|
||||
if (listtv.v_type == VAR_LIST)
|
||||
{
|
||||
/* 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;
|
||||
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,44 +489,86 @@ invoke_callback(int idx, char_u *callback, typval_T *argv)
|
||||
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(char_u *cmd, typval_T *arg)
|
||||
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)
|
||||
{
|
||||
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");
|
||||
do_cmdline_cmd(arg);
|
||||
}
|
||||
else if (STRCMP(cmd, "normal") == 0)
|
||||
{
|
||||
if (arg->v_type == VAR_STRING)
|
||||
{
|
||||
exarg_T ea;
|
||||
exarg_T ea;
|
||||
|
||||
ea.arg = arg->vval.v_string;
|
||||
ea.addr_count = 0;
|
||||
ea.forceit = TRUE; /* no mapping */
|
||||
ex_normal(&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;
|
||||
|
||||
update_screen(0);
|
||||
showruler(FALSE);
|
||||
setcursor();
|
||||
out_flush();
|
||||
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
|
||||
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)
|
||||
EMSG("E999: received normal command with non-string argument");
|
||||
}
|
||||
else if (p_verbose > 2)
|
||||
EMSG2("E999: received unknown command: %s", cmd);
|
||||
EMSG2("E905: received unknown command: %s", cmd);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -521,6 +580,7 @@ may_invoke_callback(int idx)
|
||||
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,9 +597,10 @@ may_invoke_callback(int idx)
|
||||
|
||||
if (channels[idx].ch_json_mode)
|
||||
{
|
||||
ret = channel_decode_json(msg, &typetv, &argv[1]);
|
||||
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,7 +617,7 @@ may_invoke_callback(int idx)
|
||||
{
|
||||
if (cmd != NULL)
|
||||
{
|
||||
channel_exe_cmd(cmd, &argv[1]);
|
||||
channel_exe_cmd(idx, cmd, &argv[1], &arg3);
|
||||
}
|
||||
else if (channels[idx].ch_req_callback != NULL && seq_nr != 0)
|
||||
{
|
||||
@@ -576,6 +637,7 @@ may_invoke_callback(int idx)
|
||||
{
|
||||
clear_tv(&typetv);
|
||||
clear_tv(&argv[1]);
|
||||
clear_tv(&arg3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -874,7 +936,7 @@ channel_read(int idx)
|
||||
{
|
||||
/* Todo: which channel? */
|
||||
CHERROR("%s(): cannot from channel\n", "channel_read");
|
||||
PERROR(_("E999: read from channel"));
|
||||
PERROR(_("E896: read from channel"));
|
||||
}
|
||||
}
|
||||
|
||||
|
18
src/eval.c
18
src/eval.c
@@ -16897,8 +16897,6 @@ f_sendexpr(typval_T *argvars, typval_T *rettv)
|
||||
{
|
||||
char_u *text;
|
||||
char_u *resp;
|
||||
typval_T nrtv;
|
||||
typval_T listtv;
|
||||
typval_T typetv;
|
||||
int ch_idx;
|
||||
|
||||
@@ -16906,19 +16904,9 @@ f_sendexpr(typval_T *argvars, typval_T *rettv)
|
||||
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)
|
||||
text = json_encode_nr_expr(channel_get_id(), &argvars[1]);
|
||||
if (text == NULL)
|
||||
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)
|
||||
@@ -16929,7 +16917,7 @@ f_sendexpr(typval_T *argvars, typval_T *rettv)
|
||||
resp = channel_read_block(ch_idx);
|
||||
if (resp != NULL)
|
||||
{
|
||||
channel_decode_json(resp, &typetv, rettv);
|
||||
channel_decode_json(resp, &typetv, rettv, NULL);
|
||||
vim_free(resp);
|
||||
}
|
||||
}
|
||||
|
@@ -335,7 +335,6 @@ static void ex_rundo(exarg_T *eap);
|
||||
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);
|
||||
@@ -9466,7 +9465,7 @@ ex_redir(exarg_T *eap)
|
||||
/*
|
||||
* ":redraw": force redraw
|
||||
*/
|
||||
static void
|
||||
void
|
||||
ex_redraw(exarg_T *eap)
|
||||
{
|
||||
int r = RedrawingDisabled;
|
||||
|
27
src/json.c
27
src/json.c
@@ -33,6 +33,33 @@ json_encode(typval_T *val)
|
||||
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)
|
||||
{
|
||||
|
@@ -4,7 +4,7 @@ int channel_open(char *hostname, int port_in, void (*close_cb)(void));
|
||||
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_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);
|
||||
|
@@ -46,6 +46,7 @@ void post_chdir(int local);
|
||||
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);
|
||||
|
@@ -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 : */
|
||||
|
@@ -742,6 +742,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
1229,
|
||||
/**/
|
||||
1228,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user