summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2016-05-31 22:31:23 +0200
committerBram Moolenaar <Bram@vim.org>2016-05-31 22:31:23 +0200
commit18dfb4404a618c52ee7138630a2381aed4d66eaf (patch)
tree3cfbdaa72cf42d38d0e42f01108179e115d03ee0
parentb055066a1daf12c349d6c575aff22ae4d999a157 (diff)
patch 7.4.1862v7.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)
-rw-r--r--src/eval.c134
-rw-r--r--src/testdir/test86.ok2
-rw-r--r--src/testdir/test87.ok2
-rw-r--r--src/testdir/test_viml.vim144
-rw-r--r--src/version.c2
5 files changed, 232 insertions, 52 deletions
diff --git a/src/eval.c b/src/eval.c
index d30a766531..adfe8f899a 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -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:
@@ -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 *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;
}
diff --git a/src/testdir/test86.ok b/src/testdir/test86.ok
index dc9e2a91da..6d561ecd1c 100644
--- a/src/testdir/test86.ok
+++ b/src/testdir/test86.ok
@@ -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')
diff --git a/src/testdir/test87.ok b/src/testdir/test87.ok
index b36727958f..34e9279633 100644
--- a/src/testdir/test87.ok
+++ b/src/testdir/test87.ok
@@ -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')
diff --git a/src/testdir/test_viml.vim b/src/testdir/test_viml.vim
index 10869f36a9..f1dde4b93d 100644
--- 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
index bf77c87e95..279ee6d396 100644
--- 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,