Problem: jsonencode() is not producing strict JSON.
Solution: Add jsencode() and jsdecode(). Make jsonencode() and jsondecode()
strict.
-*channel.txt* For Vim version 7.4. Last change: 2016 Feb 06
+*channel.txt* For Vim version 7.4. Last change: 2016 Feb 07
VIM REFERENCE MANUAL by Bram Moolenaar
1. Demo |channel-demo|
2. Opening a channel |channel-open|
-3. Using a JSON channel |channel-use|
+3. Using a JSON or JS channel |channel-use|
4. Vim commands |channel-commands|
5. Using a raw channel |channel-use|
6. Job control |job-control|
"mode" can be: *channel-mode*
"json" - Use JSON, see below; most convenient way. Default.
+ "js" - Use JavaScript encoding, more efficient than JSON.
"raw" - Use raw messages
*channel-callback*
func Handle(handle, msg)
echo 'Received: ' . a:msg
endfunc
- let handle = ch_open("localhost:8765", 'json', "Handle")
+ let handle = ch_open("localhost:8765", {"callback": "Handle"})
"waittime" is the time to wait for the connection to be made in milliseconds.
The default is zero, don't wait, which is useful if the server is supposed to
"timeout" is the time to wait for a request when blocking, using
ch_sendexpr(). Again in milliseconds. The default is 2000 (2 seconds).
-When "mode" is "json" the "msg" argument is the body of the received message,
-converted to Vim types.
+When "mode" is "json" or "js" the "msg" argument is the body of the received
+message, converted to Vim types.
When "mode" is "raw" the "msg" argument is the whole message as a string.
-When "mode" is "json" the "callback" is optional. When omitted it is only
-possible to receive a message after sending one.
+When "mode" is "json" or "js" the "callback" is optional. When omitted it is
+only possible to receive a message after sending one.
The handler can be added or changed later: >
call ch_setcallback(handle, {callback})
*E896* *E630* *E631*
==============================================================================
-3. Using a JSON channel *channel-use*
+3. Using a JSON or JS channel *channel-use*
If {mode} is "json" then a message can be sent synchronously like this: >
let response = ch_sendexpr(handle, {expr})
This awaits a response from the other side.
+When {mode} is "js" this works the same, except that the messages use
+JavaScript encoding. See |jsencode()| for the difference.
+
To send a message, without handling a response: >
call ch_sendexpr(handle, {expr}, 0)
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".
+evaluation fails or the result can't be encoded in JSON it is the string
+"ERROR".
Command "expr" ~
job_status({job}) String get the status of a job
job_stop({job} [, {how}]) Number stop a job
join( {list} [, {sep}]) String join {list} items into one String
+jsdecode( {string}) any decode JS style JSON
+jsencode( {expr}) String encode JS style JSON
jsondecode( {string}) any decode JSON
jsonencode( {expr}) String encode JSON
keys( {dict}) List keys in {dict}
|:wincmd|.
Only deals with the current tab page.
-
byte2line({byte}) *byte2line()*
Return the line number that contains the character at byte
count {byte} in the current buffer. This includes the
If {argdict} is given it must be a |Dictionary|. The optional
items are:
- mode "raw" or "json".
+ mode "raw", "js" or "json".
Default "json".
callback function to call for requests with a zero
sequence number. See |channel-callback|.
converted into a string like with |string()|.
The opposite function is |split()|.
+jsdecode({string}) *jsdecode()*
+ This is similar to |jsondecode()| with these differences:
+ - Object key names do not have to be in quotes.
+ - Empty items in an array (between two commas) are allowed and
+ result in v:none items.
+
+jsencode({expr}) *jsencode()*
+ This is similar to |jsonencode()| with these differences:
+ - Object key names are not in quotes.
+ - v:none items in an array result in an empty item between
+ commas.
+ For example, the Vim object:
+ [1,v:none,{"one":1}],v:none ~
+ Will be encoded as:
+ [1,,{one:1},,] ~
+ While jsonencode() would produce:
+ [1,null,{"one":1},null] ~
+ This encoding is valid for JavaScript. It is more efficient
+ than JSON, especially when using an array with optional items.
+
+
jsondecode({string}) *jsondecode()*
This parses a JSON formatted string and returns the equivalent
in Vim values. See |jsonencode()| for the relation between
JSON and Vim values.
The decoding is permissive:
- A trailing comma in an array and object is ignored.
- - An empty item in an array, two commas with nothing or white
- space in between, results in v:none.
- - When an object member name is not a string it is converted
- to a string. E.g. the number 123 is used as the string
- "123".
- More floating point numbers are recognized, e.g. "1." for
"1.0".
The result must be a valid Vim type:
used recursively: {}
v:false "false"
v:true "true"
- v:none nothing
+ v:none "null"
v:null "null"
Note that using v:none is permitted, although the JSON
standard does not allow empty items. This can be useful for
char_u *ch_callback; /* function to call when a msg is not handled */
cbq_T ch_cb_head; /* dummy node for pre-request callbacks */
- int ch_json_mode; /* TRUE for a json channel */
+ ch_mode_T ch_mode;
jsonq_T ch_json_head; /* dummy node, header for circular queue */
int ch_timeout; /* request timeout in msec */
}
/*
- * Set the json mode of channel "idx" to TRUE or FALSE.
+ * Set the json mode of channel "idx" to "ch_mode".
*/
void
-channel_set_json_mode(int idx, int json_mode)
+channel_set_json_mode(int idx, ch_mode_T ch_mode)
{
- channels[idx].ch_json_mode = json_mode;
+ channels[idx].ch_mode = ch_mode;
}
/*
js_read_T reader;
typval_T listtv;
jsonq_T *item;
- jsonq_T *head = &channels[ch_idx].ch_json_head;
+ channel_T *channel = &channels[ch_idx];
+ jsonq_T *head = &channel->ch_json_head;
int ret;
if (channel_peek(ch_idx) == NULL)
reader.js_fill = NULL;
/* reader.js_fill = channel_fill; */
reader.js_cookie = &ch_idx;
- ret = json_decode(&reader, &listtv);
+ ret = json_decode(&reader, &listtv,
+ channel->ch_mode == MODE_JS ? JSON_JS : 0);
if (ret == OK)
{
/* Only accept the response when it is a list with at least two
typval_T *tv;
typval_T err_tv;
char_u *json = NULL;
+ channel_T *channel = &channels[idx];
+ int options = channel->ch_mode == MODE_JS ? JSON_JS : 0;
/* Don't pollute the display with errors. */
++emsg_skip;
if (is_eval)
{
if (tv != NULL)
- json = json_encode_nr_expr(arg3->vval.v_number, tv);
+ json = json_encode_nr_expr(arg3->vval.v_number, tv,
+ options);
if (tv == NULL || (json != NULL && *json == NUL))
{
/* If evaluation failed or the result can't be encoded
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);
+ json = json_encode_nr_expr(arg3->vval.v_number, tv,
+ options);
}
if (json != NULL)
{
typval_T argv[3];
int seq_nr = -1;
channel_T *channel = &channels[idx];
- int json_mode = channel->ch_json_mode;
+ ch_mode_T ch_mode = channel->ch_mode;
if (channel->ch_close_cb != NULL)
/* this channel is handled elsewhere (netbeans) */
return FALSE;
- if (json_mode)
+ if (ch_mode != MODE_RAW)
{
/* Get any json message in the queue. */
if (channel_get_json(idx, -1, &listtv) == FAIL)
static void f_job_status(typval_T *argvars, typval_T *rettv);
#endif
static void f_join(typval_T *argvars, typval_T *rettv);
+static void f_jsdecode(typval_T *argvars, typval_T *rettv);
+static void f_jsencode(typval_T *argvars, typval_T *rettv);
static void f_jsondecode(typval_T *argvars, typval_T *rettv);
static void f_jsonencode(typval_T *argvars, typval_T *rettv);
static void f_keys(typval_T *argvars, typval_T *rettv);
{"job_stop", 1, 1, f_job_stop},
#endif
{"join", 1, 2, f_join},
+ {"jsdecode", 1, 1, f_jsdecode},
+ {"jsencode", 1, 1, f_jsencode},
{"jsondecode", 1, 1, f_jsondecode},
{"jsonencode", 1, 1, f_jsonencode},
{"keys", 1, 1, f_keys},
int port;
int waittime = 0;
int timeout = 2000;
- int json_mode = TRUE;
+ ch_mode_T ch_mode = MODE_JSON;
int ch_idx;
/* default: fail */
{
mode = get_dict_string(dict, (char_u *)"mode", FALSE);
if (STRCMP(mode, "raw") == 0)
- json_mode = FALSE;
- else if (STRCMP(mode, "json") != 0)
+ ch_mode = MODE_RAW;
+ else if (STRCMP(mode, "js") == 0)
+ ch_mode = MODE_JS;
+ else if (STRCMP(mode, "json") == 0)
+ ch_mode = MODE_JSON;
+ else
{
EMSG2(_(e_invarg2), mode);
return;
ch_idx = channel_open((char *)address, port, waittime, NULL);
if (ch_idx >= 0)
{
- channel_set_json_mode(ch_idx, json_mode);
+ channel_set_json_mode(ch_idx, ch_mode);
channel_set_timeout(ch_idx, timeout);
if (callback != NULL && *callback != NUL)
channel_set_callback(ch_idx, callback);
rettv->vval.v_string = NULL;
id = channel_get_id();
- text = json_encode_nr_expr(id, &argvars[1]);
+ text = json_encode_nr_expr(id, &argvars[1], 0);
if (text == NULL)
return;
rettv->vval.v_string = NULL;
}
+/*
+ * "jsdecode()" function
+ */
+ static void
+f_jsdecode(typval_T *argvars, typval_T *rettv)
+{
+ js_read_T reader;
+
+ reader.js_buf = get_tv_string(&argvars[0]);
+ reader.js_fill = NULL;
+ reader.js_used = 0;
+ if (json_decode_all(&reader, rettv, JSON_JS) != OK)
+ EMSG(_(e_invarg));
+}
+
+/*
+ * "jsencode()" function
+ */
+ static void
+f_jsencode(typval_T *argvars, typval_T *rettv)
+{
+ rettv->v_type = VAR_STRING;
+ rettv->vval.v_string = json_encode(&argvars[0], JSON_JS);
+}
+
/*
* "jsondecode()" function
*/
reader.js_buf = get_tv_string(&argvars[0]);
reader.js_fill = NULL;
reader.js_used = 0;
- if (json_decode_all(&reader, rettv) != OK)
+ if (json_decode_all(&reader, rettv, 0) != OK)
EMSG(_(e_invarg));
}
f_jsonencode(typval_T *argvars, typval_T *rettv)
{
rettv->v_type = VAR_STRING;
- rettv->vval.v_string = json_encode(&argvars[0]);
+ rettv->vval.v_string = json_encode(&argvars[0], 0);
}
/*
#include "vim.h"
#if defined(FEAT_EVAL) || defined(PROTO)
-static int json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none);
-static int json_decode_item(js_read_T *reader, typval_T *res);
+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 in allocated memory.
* The result is empty when encoding fails.
+ * "options" can be JSON_JS or zero;
*/
char_u *
-json_encode(typval_T *val)
+json_encode(typval_T *val, int options)
{
garray_T ga;
/* Store bytes in the growarray. */
ga_init2(&ga, 1, 4000);
- if (json_encode_item(&ga, val, get_copyID(), TRUE) == FAIL)
+ if (json_encode_item(&ga, val, get_copyID(), options) == FAIL)
{
vim_free(ga.ga_data);
return vim_strsave((char_u *)"");
/*
* Encode ["nr", "val"] into a JSON format string in allocated memory.
+ * "options" can be JSON_JS or zero;
* Returns NULL when out of memory.
*/
char_u *
-json_encode_nr_expr(int nr, typval_T *val)
+json_encode_nr_expr(int nr, typval_T *val, int options)
{
typval_T listtv;
typval_T nrtv;
return NULL;
}
- text = json_encode(&listtv);
+ text = json_encode(&listtv, options);
list_unref(listtv.vval.v_list);
return text;
}
}
}
+/*
+ * Return TRUE if "key" can be used without quotes.
+ * That is when it starts with a letter and only contains letters, digits and
+ * underscore.
+ */
+ static int
+is_simple_key(char_u *key)
+{
+ char_u *p;
+
+ if (!ASCII_ISALPHA(*key))
+ return FALSE;
+ for (p = key + 1; *p != NUL; ++p)
+ if (!ASCII_ISALPHA(*p) && *p != '_' && !vim_isdigit(*p))
+ return FALSE;
+ return TRUE;
+}
+
/*
* Encode "val" into "gap".
* Return FAIL or OK.
*/
static int
-json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none)
+json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
{
char_u numbuf[NUMBUFLEN];
char_u *res;
{
case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
- case VVAL_NONE: if (!allow_none)
- {
- /* TODO: better error */
- EMSG(_(e_invarg));
- return FAIL;
- }
- break;
+ case VVAL_NONE: if ((options & JSON_JS) != 0
+ && (options & JSON_NO_NONE) == 0)
+ /* empty item */
+ break;
+ /* FALLTHROUGH */
case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
}
break;
ga_append(gap, '[');
for (li = l->lv_first; li != NULL && !got_int; )
{
- if (json_encode_item(gap, &li->li_tv, copyID, TRUE)
- == FAIL)
+ if (json_encode_item(gap, &li->li_tv, copyID,
+ options & JSON_JS) == FAIL)
return FAIL;
+ if ((options & JSON_JS)
+ && li->li_next == NULL
+ && li->li_tv.v_type == VAR_SPECIAL
+ && li->li_tv.vval.v_number == VVAL_NONE)
+ /* add an extra comma if the last item is v:none */
+ ga_append(gap, ',');
li = li->li_next;
if (li != NULL)
ga_append(gap, ',');
first = FALSE;
else
ga_append(gap, ',');
- write_string(gap, hi->hi_key);
+ if ((options & JSON_JS)
+ && is_simple_key(hi->hi_key))
+ ga_concat(gap, hi->hi_key);
+ else
+ write_string(gap, hi->hi_key);
ga_append(gap, ':');
if (json_encode_item(gap, &dict_lookup(hi)->di_tv,
- copyID, FALSE) == FAIL)
+ copyID, options | JSON_NO_NONE) == FAIL)
return FAIL;
}
ga_append(gap, '}');
}
/*
- * Skip white space in "reader".
+ * Skip white space in "reader". All characters <= space are considered white
+ * space.
* Also tops up readahead when needed.
*/
static void
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
continue;
}
- if (c != ' ' && c != TAB && c != NL && c != CAR)
+ if (c == NUL || c > ' ')
break;
++reader->js_used;
}
}
static int
-json_decode_array(js_read_T *reader, typval_T *res)
+json_decode_array(js_read_T *reader, typval_T *res, int options)
{
char_u *p;
typval_T item;
break;
}
- ret = json_decode_item(reader, res == NULL ? NULL : &item);
+ ret = json_decode_item(reader, res == NULL ? NULL : &item, options);
if (ret != OK)
return ret;
if (res != NULL)
}
static int
-json_decode_object(js_read_T *reader, typval_T *res)
+json_decode_object(js_read_T *reader, typval_T *res, int options)
{
char_u *p;
typval_T tvkey;
break;
}
- ret = json_decode_item(reader, res == NULL ? NULL : &tvkey);
- if (ret != OK)
- return ret;
- if (res != NULL)
+ if ((options & JSON_JS) && reader->js_buf[reader->js_used] != '"')
{
- key = get_tv_string_buf_chk(&tvkey, buf);
- if (key == NULL || *key == NUL)
+ /* accept a key that is not in quotes */
+ key = p = reader->js_buf + reader->js_used;
+ while (*p != NUL && *p != ':' && *p > ' ')
+ ++p;
+ tvkey.v_type = VAR_STRING;
+ tvkey.vval.v_string = vim_strnsave(key, (int)(p - key));
+ reader->js_used += (int)(p - key);
+ key = tvkey.vval.v_string;
+ }
+ else
+ {
+ ret = json_decode_item(reader, res == NULL ? NULL : &tvkey,
+ options);
+ if (ret != OK)
+ return ret;
+ if (res != NULL)
{
- clear_tv(&tvkey);
- return FAIL;
+ key = get_tv_string_buf_chk(&tvkey, buf);
+ if (key == NULL || *key == NUL)
+ {
+ clear_tv(&tvkey);
+ return FAIL;
+ }
}
}
++reader->js_used;
json_skip_white(reader);
- ret = json_decode_item(reader, res == NULL ? NULL : &item);
+ ret = json_decode_item(reader, res == NULL ? NULL : &item, options);
if (ret != OK)
{
if (res != NULL)
* Return MAYBE for an incomplete message.
*/
static int
-json_decode_item(js_read_T *reader, typval_T *res)
+json_decode_item(js_read_T *reader, typval_T *res, int options)
{
char_u *p;
int len;
switch (*p)
{
case '[': /* array */
- return json_decode_array(reader, res);
+ return json_decode_array(reader, res, options);
case '{': /* object */
- return json_decode_object(reader, res);
+ return json_decode_object(reader, res, options);
case '"': /* string */
return json_decode_string(reader, res);
case ',': /* comma: empty item */
+ if ((options & JSON_JS) == 0)
+ return FAIL;
+ /* FALLTHROUGH */
case NUL: /* empty */
if (res != NULL)
{
/*
* Decode the JSON from "reader" and store the result in "res".
+ * "options" can be JSON_JS or zero;
* Return FAIL if not the whole message was consumed.
*/
int
-json_decode_all(js_read_T *reader, typval_T *res)
+json_decode_all(js_read_T *reader, typval_T *res, int options)
{
int ret;
- /* We get the end once, to avoid calling strlen() many times. */
+ /* We find the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
- ret = json_decode_item(reader, res);
+ ret = json_decode_item(reader, res, options);
if (ret != OK)
return FAIL;
json_skip_white(reader);
/*
* Decode the JSON from "reader" and store the result in "res".
+ * "options" can be JSON_JS or zero;
* Return FAIL if the message has a decoding error or the message is
* truncated. Consumes the message anyway.
*/
int
-json_decode(js_read_T *reader, typval_T *res)
+json_decode(js_read_T *reader, typval_T *res, int options)
{
int ret;
- /* We get the end once, to avoid calling strlen() many times. */
+ /* We find the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
- ret = json_decode_item(reader, res);
+ ret = json_decode_item(reader, res, options);
json_skip_white(reader);
return ret == OK ? OK : FAIL;
/*
* Decode the JSON from "reader" to find the end of the message.
+ * "options" can be JSON_JS or zero;
* Return FAIL if the message has a decoding error.
* Return MAYBE if the message is truncated, need to read more.
* This only works reliable if the message contains an object, array or
* Does not advance the reader.
*/
int
-json_find_end(js_read_T *reader)
+json_find_end(js_read_T *reader, int options)
{
int used_save = reader->js_used;
int ret;
- /* We get the end once, to avoid calling strlen() many times. */
+ /* We find the end once, to avoid calling strlen() many times. */
reader->js_end = reader->js_buf + STRLEN(reader->js_buf);
json_skip_white(reader);
- ret = json_decode_item(reader, NULL);
+ ret = json_decode_item(reader, NULL, options);
reader->js_used = used_save;
return ret;
}
/* string and incomplete string */
reader.js_buf = (char_u *)"\"hello\"";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" \"hello\" ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"\"hello";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
/* number and dash (incomplete number) */
reader.js_buf = (char_u *)"123";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"-";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
/* false, true and null, also incomplete */
reader.js_buf = (char_u *)"false";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"f";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"fa";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"fal";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"fals";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"true";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"t";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"tr";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"tru";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"null";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"n";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"nu";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"nul";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
/* object without white space */
reader.js_buf = (char_u *)"{\"a\":123}";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"{\"a\":123";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"a\":";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"a\"";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"a";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{\"";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"{";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
/* object with white space */
reader.js_buf = (char_u *)" { \"a\" : 123 } ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" { \"a\" : 123 ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { \"a\" : ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { \"a\" ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { \"a ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" { ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
/* array without white space */
reader.js_buf = (char_u *)"[\"a\",123]";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)"[\"a\",123";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"a\",";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"a\"";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"a";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[\"";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)"[";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
/* array with white space */
reader.js_buf = (char_u *)" [ \"a\" , 123 ] ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ \"a\" , ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ \"a\" ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ \"a ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
reader.js_buf = (char_u *)" [ ";
- assert(json_find_end(&reader) == MAYBE);
+ assert(json_find_end(&reader, 0) == MAYBE);
}
static int
reader.js_used = 0;
reader.js_buf = (char_u *)" [ \"a\" , 123 ";
reader.js_cookie = " [ \"a\" , 123 ] ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a\" , ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a\" ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ \"a";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
reader.js_buf = (char_u *)" [ ";
- assert(json_find_end(&reader) == OK);
+ assert(json_find_end(&reader, 0) == OK);
}
/*
/* channel.c */
void channel_gui_register_all(void);
int channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void));
-void channel_set_json_mode(int idx, int json_mode);
+void channel_set_json_mode(int idx, ch_mode_T ch_mode);
void channel_set_timeout(int idx, int timeout);
void channel_set_callback(int idx, char_u *callback);
void channel_set_req_callback(int idx, char_u *callback, int id);
/* json.c */
-char_u *json_encode(typval_T *val);
-char_u *json_encode_nr_expr(int nr, typval_T *val);
-int json_decode_all(js_read_T *reader, typval_T *res);
-int json_decode(js_read_T *reader, typval_T *res);
-int json_find_end(js_read_T *reader);
+char_u *json_encode(typval_T *val, int options);
+char_u *json_encode_nr_expr(int nr, typval_T *val, int options);
+int json_decode_all(js_read_T *reader, typval_T *res, int options);
+int json_decode(js_read_T *reader, typval_T *res, int options);
+int json_find_end(js_read_T *reader, int options);
/* vim: set ft=c : */
void *js_cookie; /* can be used by js_fill */
};
typedef struct js_reader js_read_T;
+
+/* mode for a channel */
+typedef enum
+{
+ MODE_RAW = 0,
+ MODE_JSON,
+ MODE_JS
+} ch_mode_T;
let s:varl3 = [l3, l3]
let s:jsond1 = '{"a":1,"b":"bee","c":[1,2]}'
+let s:jsd1 = '{a:1,b:"bee",c:[1,2]}'
let s:vard1 = {"a": 1, "b": "bee","c": [1,2]}
let s:jsond2 = '{"1":1,"2":{"a":"aa","b":{},"c":"cc"},"3":3}'
+let s:jsd2 = '{"1":1,"2":{a:"aa",b:{},c:"cc"},"3":3}'
let s:jsond2s = " { \"1\" : 1 , \"2\" :\n{ \"a\"\r: \"aa\" , \"b\" : {\<Tab>} , \"c\" : \"cc\" } , \"3\" : 3 }\r\n"
+let s:jsd2s = " { \"1\" : 1 , \"2\" :\n{ a\r: \"aa\" , b : {\<Tab>} , c : \"cc\" } , \"3\" : 3 }\r\n"
let s:vard2 = {"1": 1, "2": 2, "3": 3}
let d2 = {"a": "aa", "b": s:vard2, "c": "cc"}
let s:vard2["2"] = d2
let d3 = {"a": 1, "b": 2}
let s:vard3 = {"x": d3, "y": d3}
let s:jsond3 = '{"x":{"a":1,"b":2},"y":{"a":1,"b":2}}'
+let s:jsd3 = '{x:{a:1,b:2},y:{a:1,b:2}}'
+let s:vard4 = {"key": v:none}
+let s:vard4x = {"key": v:null}
+let s:jsond4 = '{"key":null}'
+let s:jsd4 = '{key:null}'
-let s:jsonvals = '[true,false,,null]'
-let s:varvals = [v:true, v:false, v:none, v:null]
+let s:jsonvals = '[true,false,null,null]'
+let s:varvals = [v:true, v:false, v:null, v:null]
-func Test_encode()
+func Test_json_encode()
call assert_equal(s:json1, jsonencode(s:var1))
call assert_equal(s:json2, jsonencode(s:var2))
call assert_equal(s:json3, jsonencode(s:var3))
call assert_equal(s:jsond1, jsonencode(s:vard1))
call assert_equal(s:jsond2, jsonencode(s:vard2))
call assert_equal(s:jsond3, jsonencode(s:vard3))
+ call assert_equal(s:jsond4, jsonencode(s:vard4))
call assert_equal(s:jsonvals, jsonencode(s:varvals))
call assert_fails('echo jsonencode(function("tr"))', 'E474:')
call assert_fails('echo jsonencode([function("tr")])', 'E474:')
- call assert_fails('echo jsonencode({"key":v:none})', 'E474:')
silent! let res = jsonencode(function("tr"))
call assert_equal("", res)
endfunc
-func Test_decode()
+func Test_json_decode()
call assert_equal(s:var1, jsondecode(s:json1))
call assert_equal(s:var2, jsondecode(s:json2))
call assert_equal(s:var3, jsondecode(s:json3))
call assert_equal(s:vard1, jsondecode(s:jsond1))
call assert_equal(s:vard2x, jsondecode(s:jsond2))
+ call assert_equal(s:vard2x, jsondecode(s:jsond2s))
call assert_equal(s:vard3, jsondecode(s:jsond3))
+ call assert_equal(s:vard4x, jsondecode(s:jsond4))
call assert_equal(s:varvals, jsondecode(s:jsonvals))
call assert_fails('call jsondecode("[1")', "E474:")
call assert_fails('call jsondecode("[1,")', "E474:")
call assert_fails('call jsondecode("[1 2]")', "E474:")
+
+ call assert_fails('call jsondecode("[1,,2]")', "E474:")
+endfunc
+
+let s:jsl5 = '[7,,,]'
+let s:varl5 = [7, v:none, v:none]
+
+func Test_js_encode()
+ call assert_equal(s:json1, jsencode(s:var1))
+ call assert_equal(s:json2, jsencode(s:var2))
+ call assert_equal(s:json3, jsencode(s:var3))
+ call assert_equal(s:json4, jsencode(s:var4))
+ call assert_equal(s:json5, jsencode(s:var5))
+
+ if has('multi_byte')
+ call assert_equal(s:jsonmb, jsencode(s:varmb))
+ endif
+
+ call assert_equal(s:jsonnr, jsencode(s:varnr))
+ if has('float')
+ call assert_equal(s:jsonfl, jsencode(s:varfl))
+ endif
+
+ call assert_equal(s:jsonl1, jsencode(s:varl1))
+ call assert_equal(s:jsonl2, jsencode(s:varl2))
+ call assert_equal(s:jsonl3, jsencode(s:varl3))
+
+ call assert_equal(s:jsd1, jsencode(s:vard1))
+ call assert_equal(s:jsd2, jsencode(s:vard2))
+ call assert_equal(s:jsd3, jsencode(s:vard3))
+ call assert_equal(s:jsd4, jsencode(s:vard4))
+
+ call assert_equal(s:jsonvals, jsencode(s:varvals))
+
+ call assert_fails('echo jsencode(function("tr"))', 'E474:')
+ call assert_fails('echo jsencode([function("tr")])', 'E474:')
+
+ silent! let res = jsencode(function("tr"))
+ call assert_equal("", res)
+
+ call assert_equal(s:jsl5, jsencode(s:varl5))
+endfunc
+
+func Test_js_decode()
+ call assert_equal(s:var1, jsdecode(s:json1))
+ call assert_equal(s:var2, jsdecode(s:json2))
+ call assert_equal(s:var3, jsdecode(s:json3))
+ call assert_equal(s:var4, jsdecode(s:json4))
+ call assert_equal(s:var5, jsdecode(s:json5))
+
+ if has('multi_byte')
+ call assert_equal(s:varmb, jsdecode(s:jsonmb))
+ endif
+
+ call assert_equal(s:varnr, jsdecode(s:jsonnr))
+ if has('float')
+ call assert_equal(s:varfl, jsdecode(s:jsonfl))
+ endif
+
+ call assert_equal(s:varl1, jsdecode(s:jsonl1))
+ call assert_equal(s:varl2x, jsdecode(s:jsonl2))
+ call assert_equal(s:varl2x, jsdecode(s:jsonl2s))
+ call assert_equal(s:varl3, jsdecode(s:jsonl3))
+
+ call assert_equal(s:vard1, jsdecode(s:jsond1))
+ call assert_equal(s:vard1, jsdecode(s:jsd1))
+ call assert_equal(s:vard2x, jsdecode(s:jsond2))
+ call assert_equal(s:vard2x, jsdecode(s:jsd2))
+ call assert_equal(s:vard2x, jsdecode(s:jsond2s))
+ call assert_equal(s:vard2x, jsdecode(s:jsd2s))
+ call assert_equal(s:vard3, jsdecode(s:jsond3))
+ call assert_equal(s:vard3, jsdecode(s:jsd3))
+ call assert_equal(s:vard4x, jsdecode(s:jsond4))
+ call assert_equal(s:vard4x, jsdecode(s:jsd4))
+
+ call assert_equal(s:varvals, jsdecode(s:jsonvals))
+
+ call assert_equal(v:true, jsdecode('true'))
+ call assert_equal(type(v:true), type(jsdecode('true')))
+ call assert_equal(v:none, jsdecode(''))
+ call assert_equal(type(v:none), type(jsdecode('')))
+ call assert_equal("", jsdecode('""'))
+
+ call assert_equal({'n': 1}, jsdecode('{"n":1,}'))
+
+ call assert_fails('call jsdecode("\"")', "E474:")
+ call assert_fails('call jsdecode("blah")', "E474:")
+ call assert_fails('call jsdecode("true blah")', "E474:")
+ call assert_fails('call jsdecode("<foobar>")', "E474:")
+
+ call assert_fails('call jsdecode("{")', "E474:")
+ call assert_fails('call jsdecode("{foobar}")', "E474:")
+ call assert_fails('call jsdecode("{\"n\",")', "E474:")
+ call assert_fails('call jsdecode("{\"n\":")', "E474:")
+ call assert_fails('call jsdecode("{\"n\":1")', "E474:")
+ call assert_fails('call jsdecode("{\"n\":1,")', "E474:")
+ call assert_fails('call jsdecode("{\"n\",1}")', "E474:")
+ call assert_fails('call jsdecode("{-}")', "E474:")
+
+ call assert_fails('call jsdecode("[foobar]")', "E474:")
+ call assert_fails('call jsdecode("[")', "E474:")
+ call assert_fails('call jsdecode("[1")', "E474:")
+ call assert_fails('call jsdecode("[1,")', "E474:")
+ call assert_fails('call jsdecode("[1 2]")', "E474:")
+
+ call assert_equal(s:varl5, jsdecode(s:jsl5))
endfunc
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 1279,
/**/
1278,
/**/
# define MAX_OPEN_CHANNELS 0
#endif
+/* 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 */
+
#ifdef FEAT_MZSCHEME
/* this is in main.c, cproto can't handle it. */
int vim_main2(int argc, char **argv);