diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/channel.c | 26 | ||||
-rw-r--r-- | src/eval.c | 47 | ||||
-rw-r--r-- | src/json.c | 138 | ||||
-rw-r--r-- | src/json_test.c | 98 | ||||
-rw-r--r-- | src/proto/channel.pro | 2 | ||||
-rw-r--r-- | src/proto/json.pro | 10 | ||||
-rw-r--r-- | src/structs.h | 8 | ||||
-rw-r--r-- | src/testdir/test_json.vim | 126 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim.h | 4 |
10 files changed, 340 insertions, 121 deletions
diff --git a/src/channel.c b/src/channel.c index a6458c65e..1d12ee722 100644 --- a/src/channel.c +++ b/src/channel.c @@ -119,7 +119,7 @@ typedef struct { 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 */ @@ -526,12 +526,12 @@ channel_open(char *hostname, int port_in, int waittime, void (*close_cb)(void)) } /* - * 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; } /* @@ -672,7 +672,8 @@ channel_parse_json(int ch_idx) 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) @@ -685,7 +686,8 @@ channel_parse_json(int ch_idx) 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 @@ -854,6 +856,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3) 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; @@ -861,7 +865,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3) 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 @@ -869,7 +874,8 @@ channel_exe_cmd(int idx, char_u *cmd, typval_T *arg2, typval_T *arg3) 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) { @@ -900,13 +906,13 @@ may_invoke_callback(int idx) 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) diff --git a/src/eval.c b/src/eval.c index bd0040e66..4b1250b59 100644 --- a/src/eval.c +++ b/src/eval.c @@ -628,6 +628,8 @@ static void f_job_stop(typval_T *argvars, typval_T *rettv); 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); @@ -8206,6 +8208,8 @@ static struct fst {"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}, @@ -9829,7 +9833,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv) int port; int waittime = 0; int timeout = 2000; - int json_mode = TRUE; + ch_mode_T ch_mode = MODE_JSON; int ch_idx; /* default: fail */ @@ -9868,8 +9872,12 @@ f_ch_open(typval_T *argvars, typval_T *rettv) { 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; @@ -9891,7 +9899,7 @@ f_ch_open(typval_T *argvars, typval_T *rettv) 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); @@ -9946,7 +9954,7 @@ f_ch_sendexpr(typval_T *argvars, typval_T *rettv) 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; @@ -14443,6 +14451,31 @@ f_join(typval_T *argvars, typval_T *rettv) } /* + * "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 */ static void @@ -14453,7 +14486,7 @@ f_jsondecode(typval_T *argvars, typval_T *rettv) 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)); } @@ -14464,7 +14497,7 @@ f_jsondecode(typval_T *argvars, typval_T *rettv) 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); } /* diff --git a/src/json.c b/src/json.c index 17eed4fa1..31bac26d5 100644 --- a/src/json.c +++ b/src/json.c @@ -16,22 +16,23 @@ #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 *)""); @@ -41,10 +42,11 @@ json_encode(typval_T *val) /* * 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; @@ -61,7 +63,7 @@ json_encode_nr_expr(int nr, typval_T *val) return NULL; } - text = json_encode(&listtv); + text = json_encode(&listtv, options); list_unref(listtv.vval.v_list); return text; } @@ -123,11 +125,29 @@ write_string(garray_T *gap, char_u *str) } /* + * 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; @@ -141,13 +161,11 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none) { 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; @@ -185,9 +203,15 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none) 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, ','); @@ -224,10 +248,14 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int allow_none) 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, '}'); @@ -265,7 +293,8 @@ fill_numbuflen(js_read_T *reader) } /* - * 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 @@ -282,7 +311,7 @@ json_skip_white(js_read_T *reader) 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; } @@ -290,7 +319,7 @@ json_skip_white(js_read_T *reader) } 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; @@ -317,7 +346,7 @@ json_decode_array(js_read_T *reader, typval_T *res) 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) @@ -347,7 +376,7 @@ json_decode_array(js_read_T *reader, typval_T *res) } 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; @@ -377,16 +406,31 @@ json_decode_object(js_read_T *reader, typval_T *res) 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; + } } } @@ -403,7 +447,7 @@ json_decode_object(js_read_T *reader, typval_T *res) ++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) @@ -569,7 +613,7 @@ json_decode_string(js_read_T *reader, typval_T *res) * 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; @@ -579,15 +623,18 @@ json_decode_item(js_read_T *reader, typval_T *res) 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) { @@ -691,17 +738,18 @@ json_decode_item(js_read_T *reader, typval_T *res) /* * 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); @@ -712,18 +760,19 @@ json_decode_all(js_read_T *reader, typval_T *res) /* * 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; @@ -731,6 +780,7 @@ json_decode(js_read_T *reader, typval_T *res) /* * 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 @@ -738,15 +788,15 @@ json_decode(js_read_T *reader, typval_T *res) * 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; } diff --git a/src/json_test.c b/src/json_test.c index f50c95608..2a28c7405 100644 --- a/src/json_test.c +++ b/src/json_test.c @@ -35,107 +35,107 @@ test_decode_find_end(void) /* 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 @@ -157,15 +157,15 @@ test_fill_called_on_find_end(void) 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); } /* diff --git a/src/proto/channel.pro b/src/proto/channel.pro index f8e4a9b9f..693d2c223 100644 --- a/src/proto/channel.pro +++ b/src/proto/channel.pro @@ -1,7 +1,7 @@ /* 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); diff --git a/src/proto/json.pro b/src/proto/json.pro index 5d2e7ba90..b98c2c9fb 100644 --- a/src/proto/json.pro +++ b/src/proto/json.pro @@ -1,7 +1,7 @@ /* 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 : */ diff --git a/src/structs.h b/src/structs.h index 5a2d6fdc4..d0823bc5e 100644 --- a/src/structs.h +++ b/src/structs.h @@ -2728,3 +2728,11 @@ struct js_reader 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; diff --git a/src/testdir/test_json.vim b/src/testdir/test_json.vim index 52cffc825..2534de113 100644 --- a/src/testdir/test_json.vim +++ b/src/testdir/test_json.vim @@ -32,9 +32,12 @@ let l3 = [1, 2] 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 @@ -42,11 +45,16 @@ let s:vard2x = {"1": 1, "2": {"a": "aa", "b": {}, "c": "cc"}, "3": 3} 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)) @@ -69,18 +77,18 @@ func Test_encode() 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)) @@ -103,7 +111,9 @@ func Test_decode() 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)) @@ -134,4 +144,110 @@ func Test_decode() 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 diff --git a/src/version.c b/src/version.c index 00b7176f5..221d0a05a 100644 --- a/src/version.c +++ b/src/version.c @@ -748,6 +748,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1279, +/**/ 1278, /**/ 1277, @@ -2317,6 +2317,10 @@ typedef int sock_T; # 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); |