# HG changeset patch # User Christian Brabandt # Date 1464727506 -7200 # Node ID e316b83892c10d451ab64497b05f7db35983e26e # Parent 724c5ba6e649478031b3fe9f9c152381bd0a2e6a commit https://github.com/vim/vim/commit/18dfb4404a618c52ee7138630a2381aed4d66eaf Author: Bram Moolenaar Date: Tue May 31 22:31:23 2016 +0200 patch 7.4.1862 Problem: string() with repeated argument does not give a result usable by eval(). Solution: Refactor echo_striong and tv2string(), moving the common part to echo_string_core(). (Ken Takata) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -445,16 +445,17 @@ static long list_idx_of_item(list_T *l, static int list_extend(list_T *l1, list_T *l2, listitem_T *bef); static int list_concat(list_T *l1, list_T *l2, typval_T *tv); static list_T *list_copy(list_T *orig, int deep, int copyID); -static char_u *list2string(typval_T *tv, int copyID); -static int list_join_inner(garray_T *gap, list_T *l, char_u *sep, int echo_style, int copyID, garray_T *join_gap); -static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo, int copyID); +static char_u *list2string(typval_T *tv, int copyID, int restore_copyID); +static int list_join_inner(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID, garray_T *join_gap); +static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); static int free_unref_items(int copyID); static dictitem_T *dictitem_copy(dictitem_T *org); static void dictitem_remove(dict_T *dict, dictitem_T *item); static dict_T *dict_copy(dict_T *orig, int deep, int copyID); static long dict_len(dict_T *d); -static char_u *dict2string(typval_T *tv, int copyID); +static char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); +static char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int dict_val); static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); static char_u *string_quote(char_u *str, int function); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); @@ -1462,7 +1463,7 @@ eval_to_string( ga_init2(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { - list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, 0); + list_join(&ga, tv.vval.v_list, (char_u *)"\n", TRUE, FALSE, 0); if (tv.vval.v_list->lv_len > 0) ga_append(&ga, NL); } @@ -6766,7 +6767,7 @@ vimlist_remove(list_T *l, listitem_T *it * May return NULL. */ static char_u * -list2string(typval_T *tv, int copyID) +list2string(typval_T *tv, int copyID, int restore_copyID) { garray_T ga; @@ -6774,7 +6775,8 @@ list2string(typval_T *tv, int copyID) return NULL; ga_init2(&ga, (int)sizeof(char), 80); ga_append(&ga, '['); - if (list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE, copyID) == FAIL) + if (list_join(&ga, tv->vval.v_list, (char_u *)", ", + FALSE, restore_copyID, copyID) == FAIL) { vim_free(ga.ga_data); return NULL; @@ -6795,6 +6797,7 @@ list_join_inner( list_T *l, char_u *sep, int echo_style, + int restore_copyID, int copyID, garray_T *join_gap) /* to keep each list item string */ { @@ -6811,10 +6814,8 @@ list_join_inner( /* Stringify each item in the list. */ for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { - if (echo_style) - s = echo_string(&item->li_tv, &tofree, numbuf, copyID); - else - s = tv2string(&item->li_tv, &tofree, numbuf, copyID); + s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID, + echo_style, restore_copyID, FALSE); if (s == NULL) return FAIL; @@ -6873,6 +6874,7 @@ list_join( list_T *l, char_u *sep, int echo_style, + int restore_copyID, int copyID) { garray_T join_ga; @@ -6883,7 +6885,8 @@ list_join( if (l->lv_len < 1) return OK; /* nothing to do */ ga_init2(&join_ga, (int)sizeof(join_T), l->lv_len); - retval = list_join_inner(gap, l, sep, echo_style, copyID, &join_ga); + retval = list_join_inner(gap, l, sep, echo_style, restore_copyID, + copyID, &join_ga); /* Dispose each item in join_ga. */ if (join_ga.ga_data != NULL) @@ -7833,7 +7836,7 @@ get_dict_number(dict_T *d, char_u *key) * May return NULL. */ static char_u * -dict2string(typval_T *tv, int copyID) +dict2string(typval_T *tv, int copyID, int restore_copyID) { garray_T ga; int first = TRUE; @@ -7868,7 +7871,8 @@ dict2string(typval_T *tv, int copyID) vim_free(tofree); } ga_concat(&ga, (char_u *)": "); - s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID); + s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID, + FALSE, restore_copyID, TRUE); if (s != NULL) ga_concat(&ga, s); vim_free(tofree); @@ -8026,16 +8030,23 @@ get_var_special_name(int nr) * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. * "numbuf" is used for a number. - * Does not put quotes around strings, as ":echo" displays values. * When "copyID" is not NULL replace recursive lists and dicts with "...". + * When both "echo_style" and "dict_val" are FALSE, put quotes around stings as + * "string()", otherwise does not put quotes around strings, as ":echo" + * displays values. + * When "restore_copyID" is FALSE, repeated items in dictionaries and lists + * are replaced with "...". * May return NULL. */ static char_u * -echo_string( +echo_string_core( typval_T *tv, char_u **tofree, char_u *numbuf, - int copyID) + int copyID, + int echo_style, + int restore_copyID, + int dict_val) { static int recurse = 0; char_u *r = NULL; @@ -8057,9 +8068,30 @@ echo_string( switch (tv->v_type) { + case VAR_STRING: + if (echo_style && !dict_val) + { + *tofree = NULL; + r = get_tv_string_buf(tv, numbuf); + } + else + { + *tofree = string_quote(tv->vval.v_string, FALSE); + r = *tofree; + } + break; + case VAR_FUNC: - *tofree = NULL; - r = tv->vval.v_string; + if (echo_style) + { + *tofree = NULL; + r = tv->vval.v_string; + } + else + { + *tofree = string_quote(tv->vval.v_string, TRUE); + r = *tofree; + } break; case VAR_PARTIAL: @@ -8114,15 +8146,20 @@ echo_string( *tofree = NULL; r = NULL; } - else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID) + else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID + && tv->vval.v_list->lv_len > 0) { *tofree = NULL; r = (char_u *)"[...]"; } else { + int old_copyID = tv->vval.v_list->lv_copyID; + tv->vval.v_list->lv_copyID = copyID; - *tofree = list2string(tv, copyID); + *tofree = list2string(tv, copyID, restore_copyID); + if (restore_copyID) + tv->vval.v_list->lv_copyID = old_copyID; r = *tofree; } break; @@ -8133,20 +8170,23 @@ echo_string( *tofree = NULL; r = NULL; } - else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID) + else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID + && tv->vval.v_dict->dv_hashtab.ht_used != 0) { *tofree = NULL; r = (char_u *)"{...}"; } else { + int old_copyID = tv->vval.v_dict->dv_copyID; tv->vval.v_dict->dv_copyID = copyID; - *tofree = dict2string(tv, copyID); + *tofree = dict2string(tv, copyID, restore_copyID); + if (restore_copyID) + tv->vval.v_dict->dv_copyID = old_copyID; r = *tofree; } break; - case VAR_STRING: case VAR_NUMBER: case VAR_UNKNOWN: case VAR_JOB: @@ -8178,6 +8218,24 @@ echo_string( * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. * "numbuf" is used for a number. + * Does not put quotes around strings, as ":echo" displays values. + * When "copyID" is not NULL replace recursive lists and dicts with "...". + * May return NULL. + */ + static char_u * +echo_string( + typval_T *tv, + char_u **tofree, + char_u *numbuf, + int copyID) +{ + return echo_string_core(tv, tofree, numbuf, copyID, TRUE, FALSE, FALSE); +} + +/* + * Return a string with the string representation of a variable. + * If the memory is allocated "tofree" is set to it, otherwise NULL. + * "numbuf" is used for a number. * Puts quotes around strings, so that they can be parsed back by eval(). * May return NULL. */ @@ -8188,31 +8246,7 @@ tv2string( char_u *numbuf, int copyID) { - switch (tv->v_type) - { - case VAR_FUNC: - *tofree = string_quote(tv->vval.v_string, TRUE); - return *tofree; - case VAR_STRING: - *tofree = string_quote(tv->vval.v_string, FALSE); - return *tofree; - case VAR_FLOAT: -#ifdef FEAT_FLOAT - *tofree = NULL; - vim_snprintf((char *)numbuf, NUMBUFLEN - 1, "%g", tv->vval.v_float); - return numbuf; -#endif - case VAR_NUMBER: - case VAR_LIST: - case VAR_DICT: - case VAR_PARTIAL: - case VAR_SPECIAL: - case VAR_JOB: - case VAR_CHANNEL: - case VAR_UNKNOWN: - break; - } - return echo_string(tv, tofree, numbuf, copyID); + return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE); } /* @@ -15182,7 +15216,7 @@ f_join(typval_T *argvars, typval_T *rett if (sep != NULL) { ga_init2(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE, 0); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok --- a/src/testdir/test86.ok +++ b/src/testdir/test86.ok @@ -484,7 +484,7 @@ psa9: psaB: psaC: -psar: +psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok --- a/src/testdir/test87.ok +++ b/src/testdir/test87.ok @@ -484,7 +484,7 @@ psa9: psaB: psaC: -psar: +psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') diff --git a/src/testdir/test_viml.vim b/src/testdir/test_viml.vim --- a/src/testdir/test_viml.vim +++ b/src/testdir/test_viml.vim @@ -1053,6 +1053,150 @@ func Test_skip() endfunc "------------------------------------------------------------------------------- +" Test 93: :echo and string() {{{1 +"------------------------------------------------------------------------------- + +func Test_echo_and_string() + " String + let a = 'foo bar' + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["foo bar", + \ "'foo bar'"], l) + + " Float + if has('float') + let a = -1.2e0 + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["-1.2", + \ "-1.2"], l) + endif + + " Funcref + redir => result + echo function('string') + echo string(function('string')) + redir END + let l = split(result, "\n") + call assert_equal(["string", + \ "function('string')"], l) + + " Recursive dictionary + let a = {} + let a["a"] = a + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {...}}", + \ "{'a': {...}}"], l) + + " Recursive list + let a = [0] + let a[0] = a + redir => result + echo a + echo string(a) + redir END + let l = split(result, "\n") + call assert_equal(["[[...]]", + \ "[[...]]"], l) + + " Empty dictionaries in a list + let a = {} + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[{}, {}, {}]", + \ "[{}, {}, {}]"], l) + + " Empty dictionaries in a dictionary + let a = {} + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {}, 'b': {}}", + \ "{'a': {}, 'b': {}}"], l) + + " Empty lists in a list + let a = [] + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[[], [], []]", + \ "[[], [], []]"], l) + + " Empty lists in a dictionary + let a = [] + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': [], 'b': []}", + \ "{'a': [], 'b': []}"], l) + + " Dictionaries in a list + let a = {"one": "yes", "two": "yes", "three": "yes"} + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {...}, {...}]", + \ "[{'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}, {'one': 'yes', 'two': 'yes', 'three': 'yes'}]"], l) + + " Dictionaries in a dictionary + let a = {"one": "yes", "two": "yes", "three": "yes"} + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {...}}", + \ "{'a': {'one': 'yes', 'two': 'yes', 'three': 'yes'}, 'b': {'one': 'yes', 'two': 'yes', 'three': 'yes'}}"], l) + + " Lists in a list + let a = [1, 2, 3] + redir => result + echo [a, a, a] + echo string([a, a, a]) + redir END + let l = split(result, "\n") + call assert_equal(["[[1, 2, 3], [...], [...]]", + \ "[[1, 2, 3], [1, 2, 3], [1, 2, 3]]"], l) + + " Lists in a dictionary + let a = [1, 2, 3] + let b = {"a": a, "b": a} + redir => result + echo b + echo string(b) + redir END + let l = split(result, "\n") + call assert_equal(["{'a': [1, 2, 3], 'b': [...]}", + \ "{'a': [1, 2, 3], 'b': [1, 2, 3]}"], l) + +endfunc + +"------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker " vim: fdt=substitute(substitute(foldtext(),\ '\\%(^+--\\)\\@<=\\(\\s*\\)\\(.\\{-}\\)\:\ \\%(\"\ \\)\\=\\(Test\ \\d*\\)\:\\s*',\ '\\3\ (\\2)\:\ \\1',\ \"\"),\ '\\(Test\\s*\\)\\(\\d\\)\\D\\@=',\ '\\1\ \\2',\ "") diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -754,6 +754,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1862, +/**/ 1861, /**/ 1860,