vim-patch:9.1.0335: String interpolation fails for List type (#28364)

Problem:  String interpolation fails for List type
Solution: use implicit string(list) for string interpolation and :put =
          (Yegappan Lakshmanan)

related: vim/vim#14529
closes: vim/vim#14556

bce51d9005

Cherry-pick eval_to_string_eap() from patch 8.2.1914.

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq
2024-04-16 10:18:24 +08:00
committed by GitHub
parent b1e8b799a5
commit e3c083832c
5 changed files with 71 additions and 10 deletions

View File

@@ -969,13 +969,12 @@ int skip_expr(char **pp, evalarg_T *const evalarg)
/// Convert "tv" to a string. /// Convert "tv" to a string.
/// ///
/// @param convert when true convert a List into a sequence of lines /// @param join_list when true convert a List into a sequence of lines.
/// and a Dict into a textual representation of the Dict.
/// ///
/// @return an allocated string. /// @return an allocated string.
static char *typval2string(typval_T *tv, bool convert) static char *typval2string(typval_T *tv, bool join_list)
{ {
if (convert && tv->v_type == VAR_LIST) { if (join_list && tv->v_type == VAR_LIST) {
garray_T ga; garray_T ga;
ga_init(&ga, (int)sizeof(char), 80); ga_init(&ga, (int)sizeof(char), 80);
if (tv->vval.v_list != NULL) { if (tv->vval.v_list != NULL) {
@@ -986,7 +985,7 @@ static char *typval2string(typval_T *tv, bool convert)
} }
ga_append(&ga, NUL); ga_append(&ga, NUL);
return (char *)ga.ga_data; return (char *)ga.ga_data;
} else if (convert && tv->v_type == VAR_DICT) { } else if (tv->v_type == VAR_LIST || tv->v_type == VAR_DICT) {
return encode_tv2string(tv, NULL); return encode_tv2string(tv, NULL);
} }
return xstrdup(tv_get_string(tv)); return xstrdup(tv_get_string(tv));
@@ -994,18 +993,20 @@ static char *typval2string(typval_T *tv, bool convert)
/// Top level evaluation function, returning a string. /// Top level evaluation function, returning a string.
/// ///
/// @param convert when true convert a List into a sequence of lines. /// @param join_list when true convert a List into a sequence of lines.
/// ///
/// @return pointer to allocated memory, or NULL for failure. /// @return pointer to allocated memory, or NULL for failure.
char *eval_to_string(char *arg, bool convert) char *eval_to_string_eap(char *arg, bool join_list, exarg_T *eap)
{ {
typval_T tv; typval_T tv;
char *retval; char *retval;
if (eval0(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL) { evalarg_T evalarg;
fill_evalarg_from_eap(&evalarg, eap, eap != NULL && eap->skip);
if (eval0(arg, &tv, NULL, &evalarg) == FAIL) {
retval = NULL; retval = NULL;
} else { } else {
retval = typval2string(&tv, convert); retval = typval2string(&tv, join_list);
tv_clear(&tv); tv_clear(&tv);
} }
clear_evalarg(&EVALARG_EVALUATE, NULL); clear_evalarg(&EVALARG_EVALUATE, NULL);
@@ -1013,6 +1014,11 @@ char *eval_to_string(char *arg, bool convert)
return retval; return retval;
} }
char *eval_to_string(char *arg, bool join_list)
{
return eval_to_string_eap(arg, join_list, NULL);
}
/// Call eval_to_string() without using current local variables and using /// Call eval_to_string() without using current local variables and using
/// textlock. /// textlock.
/// ///

View File

@@ -88,7 +88,7 @@ char *eval_one_expr_in_str(char *p, garray_T *gap, bool evaluate)
} }
if (evaluate) { if (evaluate) {
*block_end = NUL; *block_end = NUL;
char *expr_val = eval_to_string(block_start, true); char *expr_val = eval_to_string(block_start, false);
*block_end = '}'; *block_end = '}';
if (expr_val == NULL) { if (expr_val == NULL) {
return NULL; return NULL;

View File

@@ -907,6 +907,18 @@ func Test_string_interp()
#" Dict interpolation #" Dict interpolation
VAR d = {'a': 10, 'b': [1, 2]} VAR d = {'a': 10, 'b': [1, 2]}
call assert_equal("{'a': 10, 'b': [1, 2]}", $'{d}') call assert_equal("{'a': 10, 'b': [1, 2]}", $'{d}')
VAR emptydict = {}
call assert_equal("a{}b", $'a{emptydict}b')
VAR nulldict = v:_null_dict
call assert_equal("a{}b", $'a{nulldict}b')
#" List interpolation
VAR l = ['a', 'b', 'c']
call assert_equal("['a', 'b', 'c']", $'{l}')
VAR emptylist = []
call assert_equal("a[]b", $'a{emptylist}b')
VAR nulllist = v:_null_list
call assert_equal("a[]b", $'a{nulllist}b')
#" Stray closing brace. #" Stray closing brace.
call assert_fails('echo $"moo}"', 'E1278:') call assert_fails('echo $"moo}"', 'E1278:')

View File

@@ -694,6 +694,41 @@ END
END END
call assert_equal(["let d2 = {'a': 10, 'b': 'ss', 'c': {}}"], code) call assert_equal(["let d2 = {'a': 10, 'b': 'ss', 'c': {}}"], code)
" Empty dictionary
let d1 = {}
let code =<< eval trim END
let d2 = {d1}
END
call assert_equal(["let d2 = {}"], code)
" null dictionary
let d1 = v:_null_dict
let code =<< eval trim END
let d2 = {d1}
END
call assert_equal(["let d2 = {}"], code)
" Evaluate a List
let l1 = ['a', 'b', 'c']
let code =<< eval trim END
let l2 = {l1}
END
call assert_equal(["let l2 = ['a', 'b', 'c']"], code)
" Empty List
let l1 = []
let code =<< eval trim END
let l2 = {l1}
END
call assert_equal(["let l2 = []"], code)
" Null List
let l1 = v:_null_list
let code =<< eval trim END
let l2 = {l1}
END
call assert_equal(["let l2 = []"], code)
let code = 'xxx' let code = 'xxx'
let code =<< eval trim END let code =<< eval trim END
let n = {5 + let n = {5 +

View File

@@ -332,4 +332,12 @@ func Test_put_dict()
call assert_equal(["{'a': {'b': 'abc'}, 'c': [1, 2], 'd': 0z10}", ''], call assert_equal(["{'a': {'b': 'abc'}, 'c': [1, 2], 'd': 0z10}", ''],
\ getline(1, '$')) \ getline(1, '$'))
bw! bw!
endfunc
func Test_put_list()
new
let l = ['a', 'b', 'c']
put! =l
call assert_equal(['a', 'b', 'c', ''], getline(1, '$'))
bw!
endfunc endfunc