To: vim_dev@googlegroups.com Subject: Patch 7.4.1862 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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) Files: src/eval.c, src/testdir/test_viml.vim, src/testdir/test86.ok, src/testdir/test87.ok *** ../vim-7.4.1861/src/eval.c 2016-05-31 21:12:59.738705408 +0200 --- src/eval.c 2016-05-31 22:30:20.110641576 +0200 *************** *** 445,460 **** 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 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 int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); 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); --- 445,461 ---- 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, 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, 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,1468 **** 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); if (tv.vval.v_list->lv_len > 0) ga_append(&ga, NL); } --- 1463,1469 ---- ga_init2(&ga, (int)sizeof(char), 80); if (tv.vval.v_list != NULL) { ! 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,6772 **** * May return NULL. */ static char_u * ! list2string(typval_T *tv, int copyID) { garray_T ga; --- 6767,6773 ---- * May return NULL. */ static char_u * ! list2string(typval_T *tv, int copyID, int restore_copyID) { garray_T ga; *************** *** 6774,6780 **** 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) { vim_free(ga.ga_data); return NULL; --- 6775,6782 ---- return NULL; ga_init2(&ga, (int)sizeof(char), 80); ga_append(&ga, '['); ! if (list_join(&ga, tv->vval.v_list, (char_u *)", ", ! FALSE, restore_copyID, copyID) == FAIL) { vim_free(ga.ga_data); return NULL; *************** *** 6795,6800 **** --- 6797,6803 ---- 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,6820 **** /* 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); if (s == NULL) return FAIL; --- 6814,6821 ---- /* Stringify each item in the list. */ for (item = l->lv_first; item != NULL && !got_int; item = item->li_next) { ! s = echo_string_core(&item->li_tv, &tofree, numbuf, copyID, ! echo_style, restore_copyID, FALSE); if (s == NULL) return FAIL; *************** *** 6873,6878 **** --- 6874,6880 ---- list_T *l, char_u *sep, int echo_style, + int restore_copyID, int copyID) { garray_T join_ga; *************** *** 6883,6889 **** 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); /* Dispose each item in join_ga. */ if (join_ga.ga_data != NULL) --- 6885,6892 ---- 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, restore_copyID, ! copyID, &join_ga); /* Dispose each item in join_ga. */ if (join_ga.ga_data != NULL) *************** *** 7833,7839 **** * May return NULL. */ static char_u * ! dict2string(typval_T *tv, int copyID) { garray_T ga; int first = TRUE; --- 7836,7842 ---- * May return NULL. */ static char_u * ! dict2string(typval_T *tv, int copyID, int restore_copyID) { garray_T ga; int first = TRUE; *************** *** 7868,7874 **** vim_free(tofree); } ga_concat(&ga, (char_u *)": "); ! s = tv2string(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID); if (s != NULL) ga_concat(&ga, s); vim_free(tofree); --- 7871,7878 ---- vim_free(tofree); } ga_concat(&ga, (char_u *)": "); ! 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,8041 **** * 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) { static int recurse = 0; char_u *r = NULL; --- 8030,8052 ---- * 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. * 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_core( typval_T *tv, char_u **tofree, char_u *numbuf, ! int copyID, ! int echo_style, ! int restore_copyID, ! int dict_val) { static int recurse = 0; char_u *r = NULL; *************** *** 8057,8065 **** switch (tv->v_type) { case VAR_FUNC: ! *tofree = NULL; ! r = tv->vval.v_string; break; case VAR_PARTIAL: --- 8068,8097 ---- 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: ! 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,8128 **** *tofree = NULL; r = NULL; } ! else if (copyID != 0 && tv->vval.v_list->lv_copyID == copyID) { *tofree = NULL; r = (char_u *)"[...]"; } else { tv->vval.v_list->lv_copyID = copyID; ! *tofree = list2string(tv, copyID); r = *tofree; } break; --- 8146,8165 ---- *tofree = NULL; r = NULL; } ! 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, restore_copyID); ! if (restore_copyID) ! tv->vval.v_list->lv_copyID = old_copyID; r = *tofree; } break; *************** *** 8133,8152 **** *tofree = NULL; r = NULL; } ! else if (copyID != 0 && tv->vval.v_dict->dv_copyID == copyID) { *tofree = NULL; r = (char_u *)"{...}"; } else { tv->vval.v_dict->dv_copyID = copyID; ! *tofree = dict2string(tv, copyID); r = *tofree; } break; - case VAR_STRING: case VAR_NUMBER: case VAR_UNKNOWN: case VAR_JOB: --- 8170,8192 ---- *tofree = NULL; r = NULL; } ! 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, restore_copyID); ! if (restore_copyID) ! tv->vval.v_dict->dv_copyID = old_copyID; r = *tofree; } break; case VAR_NUMBER: case VAR_UNKNOWN: case VAR_JOB: *************** *** 8178,8183 **** --- 8218,8241 ---- * 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,8218 **** 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); } /* --- 8246,8252 ---- char_u *numbuf, int copyID) { ! return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE); } /* *************** *** 15182,15188 **** if (sep != NULL) { ga_init2(&ga, (int)sizeof(char), 80); ! list_join(&ga, argvars[0].vval.v_list, sep, TRUE, 0); ga_append(&ga, NUL); rettv->vval.v_string = (char_u *)ga.ga_data; } --- 15216,15222 ---- if (sep != NULL) { ga_init2(&ga, (int)sizeof(char), 80); ! 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; } *** ../vim-7.4.1861/src/testdir/test_viml.vim 2016-03-19 18:52:14.938587155 +0100 --- src/testdir/test_viml.vim 2016-05-31 22:23:35.038647149 +0200 *************** *** 1053,1058 **** --- 1053,1202 ---- 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',\ "") *** ../vim-7.4.1861/src/testdir/test86.ok 2016-05-25 20:38:49.757864614 +0200 --- src/testdir/test86.ok 2016-05-31 22:02:40.422664407 +0200 *************** *** 484,490 **** psaA: psaB: psaC: ! psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') --- 484,490 ---- psaA: psaB: psaC: ! psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') *** ../vim-7.4.1861/src/testdir/test87.ok 2016-05-25 20:38:49.757864614 +0200 --- src/testdir/test87.ok 2016-05-31 22:03:23.554663813 +0200 *************** *** 484,490 **** psaA: psaB: psaC: ! psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') --- 484,490 ---- psaA: psaB: psaC: ! psar: s(a): function('Args') s(pa1): function('Args', ['abcArgsPA1']) s(pa2): function('Args') *** ../vim-7.4.1861/src/version.c 2016-05-31 21:37:32.962685143 +0200 --- src/version.c 2016-05-31 21:40:56.750682340 +0200 *************** *** 755,756 **** --- 755,758 ---- { /* Add new patch number below this line */ + /**/ + 1862, /**/ -- This planet has -- or rather had -- a problem, which was this: most of the people living on it were unhappy for pretty much of the time. Many solutions were suggested for this problem, but most of these were largely concerned with the movements of small green pieces of paper, which is odd because on the whole it wasn't the small green pieces of paper that were unhappy. -- Douglas Adams, "The Hitchhiker's Guide to the Galaxy" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///