Skip to content

Commit f1f0792

Browse files
committed
patch 7.4.2258
Problem: Two JSON messages are sent without a separator. Solution: Separate messages with a NL. (closes #1001)
1 parent 9f28953 commit f1f0792

File tree

7 files changed

+53
-18
lines changed

7 files changed

+53
-18
lines changed

runtime/doc/channel.txt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
*channel.txt* For Vim version 7.4. Last change: 2016 Jul 15
1+
*channel.txt* For Vim version 7.4. Last change: 2016 Aug 26
22

33

44
VIM REFERENCE MANUAL by Bram Moolenaar
@@ -247,9 +247,15 @@ In which {number} is different every time. It must be used in the response
247247
This way Vim knows which sent message matches with which received message and
248248
can call the right handler. Also when the messages arrive out of order.
249249

250+
A newline character is terminating the JSON text. This can be used to
251+
separate the read text. For example, in Python:
252+
splitidx = read_text.find('\n')
253+
message = read_text[:splitidx]
254+
rest = read_text[splitidx + 1:]
255+
250256
The sender must always send valid JSON to Vim. Vim can check for the end of
251257
the message by parsing the JSON. It will only accept the message if the end
252-
was received.
258+
was received. A newline after the message is optional.
253259

254260
When the process wants to send a message to Vim without first receiving a
255261
message, it must use the number zero:

src/channel.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2165,7 +2165,7 @@ channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
21652165
int id = argv[id_idx].vval.v_number;
21662166

21672167
if (tv != NULL)
2168-
json = json_encode_nr_expr(id, tv, options);
2168+
json = json_encode_nr_expr(id, tv, options | JSON_NL);
21692169
if (tv == NULL || (json != NULL && *json == NUL))
21702170
{
21712171
/* 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)
21752175
err_tv.v_type = VAR_STRING;
21762176
err_tv.vval.v_string = (char_u *)"ERROR";
21772177
tv = &err_tv;
2178-
json = json_encode_nr_expr(id, tv, options);
2178+
json = json_encode_nr_expr(id, tv, options | JSON_NL);
21792179
}
21802180
if (json != NULL)
21812181
{
@@ -3500,7 +3500,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
35003500

35013501
id = ++channel->ch_last_msg_id;
35023502
text = json_encode_nr_expr(id, &argvars[1],
3503-
ch_mode == MODE_JS ? JSON_JS : 0);
3503+
(ch_mode == MODE_JS ? JSON_JS : 0) | JSON_NL);
35043504
if (text == NULL)
35053505
return;
35063506

src/json.c

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,28 @@
2121
static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int options);
2222
static int json_decode_item(js_read_T *reader, typval_T *res, int options);
2323

24+
/*
25+
* Encode "val" into a JSON format string.
26+
* The result is added to "gap"
27+
* Returns FAIL on failure and makes gap->ga_data empty.
28+
*/
29+
static int
30+
json_encode_gap(garray_T *gap, typval_T *val, int options)
31+
{
32+
if (json_encode_item(gap, val, get_copyID(), options) == FAIL)
33+
{
34+
ga_clear(gap);
35+
gap->ga_data = vim_strsave((char_u *)"");
36+
return FAIL;
37+
}
38+
return OK;
39+
}
40+
2441
/*
2542
* Encode "val" into a JSON format string.
2643
* The result is in allocated memory.
2744
* The result is empty when encoding fails.
28-
* "options" can be JSON_JS or zero;
45+
* "options" can contain JSON_JS, JSON_NO_NONE and JSON_NL.
2946
*/
3047
char_u *
3148
json_encode(typval_T *val, int options)
@@ -34,25 +51,21 @@ json_encode(typval_T *val, int options)
3451

3552
/* Store bytes in the growarray. */
3653
ga_init2(&ga, 1, 4000);
37-
if (json_encode_item(&ga, val, get_copyID(), options) == FAIL)
38-
{
39-
vim_free(ga.ga_data);
40-
return vim_strsave((char_u *)"");
41-
}
54+
json_encode_gap(&ga, val, options);
4255
return ga.ga_data;
4356
}
4457

4558
/*
4659
* Encode ["nr", "val"] into a JSON format string in allocated memory.
47-
* "options" can be JSON_JS or zero;
60+
* "options" can contain JSON_JS, JSON_NO_NONE and JSON_NL.
4861
* Returns NULL when out of memory.
4962
*/
5063
char_u *
5164
json_encode_nr_expr(int nr, typval_T *val, int options)
5265
{
5366
typval_T listtv;
5467
typval_T nrtv;
55-
char_u *text;
68+
garray_T ga;
5669

5770
nrtv.v_type = VAR_NUMBER;
5871
nrtv.vval.v_number = nr;
@@ -65,9 +78,11 @@ json_encode_nr_expr(int nr, typval_T *val, int options)
6578
return NULL;
6679
}
6780

68-
text = json_encode(&listtv, options);
81+
ga_init2(&ga, 1, 4000);
82+
if (json_encode_gap(&ga, &listtv, options) == OK && (options & JSON_NL))
83+
ga_append(&ga, '\n');
6984
list_unref(listtv.vval.v_list);
70-
return text;
85+
return ga.ga_data;
7186
}
7287

7388
static void

src/testdir/test_channel.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ def handle(self):
3838
print("received: {0}".format(received))
3939

4040
# We may receive two messages at once. Take the part up to the
41-
# matching "]" (recognized by finding "][").
41+
# newline, which should be after the matching "]".
4242
todo = received
4343
while todo != '':
44-
splitidx = todo.find('][')
44+
splitidx = todo.find('\n')
4545
if splitidx < 0:
4646
used = todo
4747
todo = ''
4848
else:
49-
used = todo[:splitidx + 1]
49+
used = todo[:splitidx]
5050
todo = todo[splitidx + 1:]
5151
if used != received:
5252
print("using: {0}".format(used))

src/testdir/test_channel.vim

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,17 @@ func Ch_communicate(port)
5555
call WaitFor('exists("g:split")')
5656
call assert_equal(123, g:split)
5757

58+
" string with ][ should work
59+
call assert_equal('this][that', ch_evalexpr(handle, 'echo this][that'))
60+
61+
" sending three messages quickly then reading should work
62+
for i in range(3)
63+
call ch_sendexpr(handle, 'echo hello ' . i)
64+
endfor
65+
call assert_equal('hello 0', ch_read(handle)[1])
66+
call assert_equal('hello 1', ch_read(handle)[1])
67+
call assert_equal('hello 2', ch_read(handle)[1])
68+
5869
" Request that triggers sending two ex commands. These will usually be
5970
" handled before getting the response, but it's not guaranteed, thus wait a
6071
" tiny bit for the commands to get executed.

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -763,6 +763,8 @@ static char *(features[]) =
763763

764764
static int included_patches[] =
765765
{ /* Add new patch number below this line */
766+
/**/
767+
2258,
766768
/**/
767769
2257,
768770
/**/

src/vim.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2440,6 +2440,7 @@ typedef enum
24402440
/* Options for json_encode() and json_decode. */
24412441
#define JSON_JS 1 /* use JS instead of JSON */
24422442
#define JSON_NO_NONE 2 /* v:none item not allowed */
2443+
#define JSON_NL 4 /* append a NL */
24432444

24442445
/* Used for flags of do_in_path() */
24452446
#define DIP_ALL 0x01 /* all matches, not just the first one */

0 commit comments

Comments
 (0)