]> granicus.if.org Git - vim/commitdiff
patch 7.4.1862 v7.4.1862
authorBram Moolenaar <Bram@vim.org>
Tue, 31 May 2016 20:31:23 +0000 (22:31 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 31 May 2016 20:31:23 +0000 (22:31 +0200)
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)

src/eval.c
src/testdir/test86.ok
src/testdir/test87.ok
src/testdir/test_viml.vim
src/version.c

index d30a766531788b53bbfb9ef6f04aa1748a757399..adfe8f899a8e684f639dd0584db5eb78f5a12787 100644 (file)
@@ -445,16 +445,17 @@ static long list_idx_of_item(list_T *l, listitem_T *item);
 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 *item, listitem_T *item2)
  * 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:
@@ -8174,6 +8214,24 @@ echo_string(
     return r;
 }
 
+/*
+ * 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.
@@ -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 *rettv)
     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;
     }
index dc9e2a91da4819fa2e8d3a47a25aaf2cfcdf6b4c..6d561ecd1cec5376e8927b4b59ff401096373171 100644 (file)
@@ -484,7 +484,7 @@ psa9: <vim.Function 'SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_reb
 psaA: <vim.Function 'SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=True>
 psaB: <vim.Function 'SelfArgs', args=['abcArgsPSAB']>
 psaC: <vim.Function 'SelfArgs'>
-psar: <vim.Function 'SelfArgs', args=[{'abcArgsPSAr2': [{'rec': function('SelfArgs', [{...}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], self={'rec': function('SelfArgs', [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}>
+psar: <vim.Function 'SelfArgs', args=[{'abcArgsPSAr2': [{'rec': function('SelfArgs', [{...}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], self={'rec': function('SelfArgs', [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}]}>
 s(a): function('Args')
 s(pa1): function('Args', ['abcArgsPA1'])
 s(pa2): function('Args')
index b36727958f76ecacc7cf3073ef3ad2f7adcc871c..34e9279633eb717960975e3ff3fc7ab810e8131f 100644 (file)
@@ -484,7 +484,7 @@ psa9: <vim.Function 'SelfArgs', self={'abcSelfPSA9': 'abcSelfPSA9Val'}, auto_reb
 psaA: <vim.Function 'SelfArgs', args=['abcArgsPSAA'], self={'abcSelfPSAA': 'abcSelfPSAAVal'}, auto_rebind=True>
 psaB: <vim.Function 'SelfArgs', args=['abcArgsPSAB']>
 psaC: <vim.Function 'SelfArgs'>
-psar: <vim.Function 'SelfArgs', args=[{'abcArgsPSAr2': [{'rec': function('SelfArgs', [{...}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], self={'rec': function('SelfArgs', [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}>
+psar: <vim.Function 'SelfArgs', args=[{'abcArgsPSAr2': [{'rec': function('SelfArgs', [{...}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{...}]}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], self={'rec': function('SelfArgs', [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}], {...}), 'self': {...}, 'abcSelfPSAr': 'abcSelfPSArVal', 'args': [{'abcArgsPSAr2': [{...}, {...}], 'abcArgsPSAr': 'abcArgsPSArVal'}]}>
 s(a): function('Args')
 s(pa1): function('Args', ['abcArgsPA1'])
 s(pa2): function('Args')
index 10869f36a9e5125803680b4d1e361810d8480245..f1dde4b93d2f3cd9ac4418758237b281acb75cd3 100644 (file)
@@ -1052,6 +1052,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
index bf77c87e95f4ed4c68ee29882467a68b95489ddd..279ee6d3961765b51fa64028e96780937d0462cc 100644 (file)
@@ -753,6 +753,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1862,
 /**/
     1861,
 /**/