1
0
forked from aniani/vim

patch 7.4.2258

Problem:    Two JSON messages are sent without a separator.
Solution:   Separate messages with a NL. (closes #1001)
This commit is contained in:
Bram Moolenaar
2016-08-26 17:58:53 +02:00
parent 9f28953f0c
commit f1f0792e55
7 changed files with 53 additions and 18 deletions

View File

@@ -1,4 +1,4 @@
*channel.txt* For Vim version 7.4. Last change: 2016 Jul 15 *channel.txt* For Vim version 7.4. Last change: 2016 Aug 26
VIM REFERENCE MANUAL by Bram Moolenaar VIM REFERENCE MANUAL by Bram Moolenaar
@@ -247,9 +247,15 @@ In which {number} is different every time. It must be used in the response
This way Vim knows which sent message matches with which received message and This way Vim knows which sent message matches with which received message and
can call the right handler. Also when the messages arrive out of order. can call the right handler. Also when the messages arrive out of order.
A newline character is terminating the JSON text. This can be used to
separate the read text. For example, in Python:
splitidx = read_text.find('\n')
message = read_text[:splitidx]
rest = read_text[splitidx + 1:]
The sender must always send valid JSON to Vim. Vim can check for the end of The sender must always send valid JSON to Vim. Vim can check for the end of
the message by parsing the JSON. It will only accept the message if the end the message by parsing the JSON. It will only accept the message if the end
was received. was received. A newline after the message is optional.
When the process wants to send a message to Vim without first receiving a When the process wants to send a message to Vim without first receiving a
message, it must use the number zero: message, it must use the number zero:

View File

@@ -2165,7 +2165,7 @@ channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
int id = argv[id_idx].vval.v_number; int id = argv[id_idx].vval.v_number;
if (tv != NULL) if (tv != NULL)
json = json_encode_nr_expr(id, tv, options); json = json_encode_nr_expr(id, tv, options | JSON_NL);
if (tv == NULL || (json != NULL && *json == NUL)) if (tv == NULL || (json != NULL && *json == NUL))
{ {
/* If evaluation failed or the result can't be encoded /* If evaluation failed or the result can't be encoded
@@ -2175,7 +2175,7 @@ channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
err_tv.v_type = VAR_STRING; err_tv.v_type = VAR_STRING;
err_tv.vval.v_string = (char_u *)"ERROR"; err_tv.vval.v_string = (char_u *)"ERROR";
tv = &err_tv; tv = &err_tv;
json = json_encode_nr_expr(id, tv, options); json = json_encode_nr_expr(id, tv, options | JSON_NL);
} }
if (json != NULL) if (json != NULL)
{ {
@@ -3500,7 +3500,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
id = ++channel->ch_last_msg_id; id = ++channel->ch_last_msg_id;
text = json_encode_nr_expr(id, &argvars[1], text = json_encode_nr_expr(id, &argvars[1],
ch_mode == MODE_JS ? JSON_JS : 0); (ch_mode == MODE_JS ? JSON_JS : 0) | JSON_NL);
if (text == NULL) if (text == NULL)
return; return;

View File

@@ -21,11 +21,28 @@
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options); static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options);
static int json_decode_item(js_read_T *reader, typval_T *res, int options); static int json_decode_item(js_read_T *reader, typval_T *res, int options);
/*
* Encode "val" into a JSON format string.
* The result is added to "gap"
* Returns FAIL on failure and makes gap->ga_data empty.
*/
static int
json_encode_gap(garray_T *gap, typval_T *val, int options)
{
if (json_encode_item(gap, val, get_copyID(), options) == FAIL)
{
ga_clear(gap);
gap->ga_data = vim_strsave((char_u *)"");
return FAIL;
}
return OK;
}
/* /*
* Encode "val" into a JSON format string. * Encode "val" into a JSON format string.
* The result is in allocated memory. * The result is in allocated memory.
* The result is empty when encoding fails. * The result is empty when encoding fails.
* "options" can be JSON_JS or zero; * "options" can contain JSON_JS, JSON_NO_NONE and JSON_NL.
*/ */
char_u * char_u *
json_encode(typval_T *val, int options) json_encode(typval_T *val, int options)
@@ -34,17 +51,13 @@ json_encode(typval_T *val, int options)
/* Store bytes in the growarray. */ /* Store bytes in the growarray. */
ga_init2(&ga, 1, 4000); ga_init2(&ga, 1, 4000);
if (json_encode_item(&ga, val, get_copyID(), options) == FAIL) json_encode_gap(&ga, val, options);
{
vim_free(ga.ga_data);
return vim_strsave((char_u *)"");
}
return ga.ga_data; return ga.ga_data;
} }
/* /*
* Encode ["nr", "val"] into a JSON format string in allocated memory. * Encode ["nr", "val"] into a JSON format string in allocated memory.
* "options" can be JSON_JS or zero; * "options" can contain JSON_JS, JSON_NO_NONE and JSON_NL.
* Returns NULL when out of memory. * Returns NULL when out of memory.
*/ */
char_u * char_u *
@@ -52,7 +65,7 @@ json_encode_nr_expr(int nr, typval_T *val, int options)
{ {
typval_T listtv; typval_T listtv;
typval_T nrtv; typval_T nrtv;
char_u *text; garray_T ga;
nrtv.v_type = VAR_NUMBER; nrtv.v_type = VAR_NUMBER;
nrtv.vval.v_number = nr; nrtv.vval.v_number = nr;
@@ -65,9 +78,11 @@ json_encode_nr_expr(int nr, typval_T *val, int options)
return NULL; return NULL;
} }
text = json_encode(&listtv, options); ga_init2(&ga, 1, 4000);
if (json_encode_gap(&ga, &listtv, options) == OK && (options & JSON_NL))
ga_append(&ga, '\n');
list_unref(listtv.vval.v_list); list_unref(listtv.vval.v_list);
return text; return ga.ga_data;
} }
static void static void

View File

@@ -38,15 +38,15 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
print("received: {0}".format(received)) print("received: {0}".format(received))
# We may receive two messages at once. Take the part up to the # We may receive two messages at once. Take the part up to the
# matching "]" (recognized by finding "]["). # newline, which should be after the matching "]".
todo = received todo = received
while todo != '': while todo != '':
splitidx = todo.find('][') splitidx = todo.find('\n')
if splitidx < 0: if splitidx < 0:
used = todo used = todo
todo = '' todo = ''
else: else:
used = todo[:splitidx + 1] used = todo[:splitidx]
todo = todo[splitidx + 1:] todo = todo[splitidx + 1:]
if used != received: if used != received:
print("using: {0}".format(used)) print("using: {0}".format(used))

View File

@@ -55,6 +55,17 @@ func Ch_communicate(port)
call WaitFor('exists("g:split")') call WaitFor('exists("g:split")')
call assert_equal(123, g:split) call assert_equal(123, g:split)
" string with ][ should work
call assert_equal('this][that', ch_evalexpr(handle, 'echo this][that'))
" sending three messages quickly then reading should work
for i in range(3)
call ch_sendexpr(handle, 'echo hello ' . i)
endfor
call assert_equal('hello 0', ch_read(handle)[1])
call assert_equal('hello 1', ch_read(handle)[1])
call assert_equal('hello 2', ch_read(handle)[1])
" Request that triggers sending two ex commands. These will usually be " Request that triggers sending two ex commands. These will usually be
" handled before getting the response, but it's not guaranteed, thus wait a " handled before getting the response, but it's not guaranteed, thus wait a
" tiny bit for the commands to get executed. " tiny bit for the commands to get executed.

View File

@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2258,
/**/ /**/
2257, 2257,
/**/ /**/

View File

@@ -2440,6 +2440,7 @@ typedef enum
/* Options for json_encode() and json_decode. */ /* Options for json_encode() and json_decode. */
#define JSON_JS 1 /* use JS instead of JSON */ #define JSON_JS 1 /* use JS instead of JSON */
#define JSON_NO_NONE 2 /* v:none item not allowed */ #define JSON_NO_NONE 2 /* v:none item not allowed */
#define JSON_NL 4 /* append a NL */
/* Used for flags of do_in_path() */ /* Used for flags of do_in_path() */
#define DIP_ALL 0x01 /* all matches, not just the first one */ #define DIP_ALL 0x01 /* all matches, not just the first one */