]> granicus.if.org Git - vim/commitdiff
patch 7.4.2258 v7.4.2258
authorBram Moolenaar <Bram@vim.org>
Fri, 26 Aug 2016 15:58:53 +0000 (17:58 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 26 Aug 2016 15:58:53 +0000 (17:58 +0200)
Problem:    Two JSON messages are sent without a separator.
Solution:   Separate messages with a NL. (closes #1001)

runtime/doc/channel.txt
src/channel.c
src/json.c
src/testdir/test_channel.py
src/testdir/test_channel.vim
src/version.c
src/vim.h

index 9a5d36da268c40782ad5c1bfbc9bf71c03893cc5..f0c56e9405a0bb809a3b050c4a8f9bbe84957984 100644 (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
@@ -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
 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 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
 message, it must use the number zero:
index 4435248853a6665422149ee44f88d7a4d69ea8cb..74885742384adc8385baf7fee4f35a94e33a9b5b 100644 (file)
@@ -2165,7 +2165,7 @@ channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
                int id = argv[id_idx].vval.v_number;
 
                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 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.vval.v_string = (char_u *)"ERROR";
                    tv = &err_tv;
-                   json = json_encode_nr_expr(id, tv, options);
+                   json = json_encode_nr_expr(id, tv, options | JSON_NL);
                }
                if (json != NULL)
                {
@@ -3500,7 +3500,7 @@ ch_expr_common(typval_T *argvars, typval_T *rettv, int eval)
 
     id = ++channel->ch_last_msg_id;
     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)
        return;
 
index 89c2fd25457c4fbe59902ba2bbe10fc4e98cd7b5..bdfeb83e2c73435f4af359cd3867211ab240a7de 100644 (file)
 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);
 
+/*
+ * 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.
  * The result is in allocated memory.
  * 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 *
 json_encode(typval_T *val, int options)
@@ -34,17 +51,13 @@ json_encode(typval_T *val, int options)
 
     /* Store bytes in the growarray. */
     ga_init2(&ga, 1, 4000);
-    if (json_encode_item(&ga, val, get_copyID(), options) == FAIL)
-    {
-       vim_free(ga.ga_data);
-       return vim_strsave((char_u *)"");
-    }
+    json_encode_gap(&ga, val, options);
     return ga.ga_data;
 }
 
 /*
  * 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.
  */
     char_u *
@@ -52,7 +65,7 @@ json_encode_nr_expr(int nr, typval_T *val, int options)
 {
     typval_T   listtv;
     typval_T   nrtv;
-    char_u     *text;
+    garray_T   ga;
 
     nrtv.v_type = VAR_NUMBER;
     nrtv.vval.v_number = nr;
@@ -65,9 +78,11 @@ json_encode_nr_expr(int nr, typval_T *val, int options)
        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);
-    return text;
+    return ga.ga_data;
 }
 
     static void
index b5a912c078c603ddc57e6256d5b9b538529b136d..07a22418a8db8f645025c0dd72300a887cba27b1 100644 (file)
@@ -38,15 +38,15 @@ class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
             print("received: {0}".format(received))
 
             # 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
             while todo != '':
-                splitidx = todo.find('][')
+                splitidx = todo.find('\n')
                 if splitidx < 0:
                      used = todo
                      todo = ''
                 else:
-                     used = todo[:splitidx + 1]
+                     used = todo[:splitidx]
                      todo = todo[splitidx + 1:]
                 if used != received:
                     print("using: {0}".format(used))
index fff9ebd6c6096acb6bafc62c31cfcd303a2e82b1..964024474b6881d68d721a3cf3b8d2601182bc2a 100644 (file)
@@ -55,6 +55,17 @@ func Ch_communicate(port)
   call WaitFor('exists("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
   " handled before getting the response, but it's not guaranteed, thus wait a
   " tiny bit for the commands to get executed.
index 9feaa0280874808c286ca3082cc0f596724c69b1..83bf09bc380278bcd062fecb623c70fcc8d9eee3 100644 (file)
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2258,
 /**/
     2257,
 /**/
index 8d49dcd183b37739da6d4f761948379037493bb8..41c4bac709d75fc852bf498fd5db0ba8d2c80938 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -2440,6 +2440,7 @@ typedef enum
 /* Options for json_encode() and json_decode. */
 #define JSON_JS                1   /* use JS instead of JSON */
 #define JSON_NO_NONE   2   /* v:none item not allowed */
+#define JSON_NL                4   /* append a NL */
 
 /* Used for flags of do_in_path() */
 #define DIP_ALL            0x01        /* all matches, not just the first one */