vim-patch:9.1.0329: String interpolation fails for Dict type (#28335)

Problem:  String interpolation fails for Dict type
Solution: Support Dict data type properly, also support :put =Dict
          (without having to convert it to string() first)
          (Yegappan Lakshmanan)

fixes: vim/vim#14529
closes: vim/vim#14541

f01493c550

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq 2024-04-15 07:11:39 +08:00 committed by GitHub
parent 7180ef6901
commit 43f8d7e3ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 62 additions and 48 deletions

View File

@ -7876,10 +7876,10 @@ string({expr}) *string()*
for infinite and NaN floating-point values representations for infinite and NaN floating-point values representations
which use |str2float()|. Strings are also dumped literally, which use |str2float()|. Strings are also dumped literally,
only single quote is escaped, which does not allow using YAML only single quote is escaped, which does not allow using YAML
for parsing back binary strings. |eval()| should always work for for parsing back binary strings. |eval()| should always work
strings and floats though and this is the only official for strings and floats though, and this is the only official
method, use |msgpackdump()| or |json_encode()| if you need to method. Use |msgpackdump()| or |json_encode()| if you need to
share data with other application. share data with other applications.
strlen({string}) *strlen()* strlen({string}) *strlen()*
The result is a Number, which is the length of the String The result is a Number, which is the length of the String

View File

@ -1291,8 +1291,8 @@ expression (like with the "/" command).
The expression must evaluate to a String. A Number is always automatically The expression must evaluate to a String. A Number is always automatically
converted to a String. For the "p" and ":put" command, if the result is a converted to a String. For the "p" and ":put" command, if the result is a
Float it's converted into a String. If the result is a List each element is Float it's converted into a String. If the result is a List each element is
turned into a String and used as a line. A Dictionary or FuncRef results in turned into a String and used as a line. A Dictionary is converted into a
an error message (use string() to convert). String. A FuncRef results in an error message (use string() to convert).
If the "= register is used for the "p" command, the String is split up at <NL> If the "= register is used for the "p" command, the String is split up at <NL>
characters. If the String ends in a <NL>, it is regarded as a linewise characters. If the String ends in a <NL>, it is regarded as a linewise

View File

@ -9353,10 +9353,10 @@ function vim.fn.stridx(haystack, needle, start) end
--- for infinite and NaN floating-point values representations --- for infinite and NaN floating-point values representations
--- which use |str2float()|. Strings are also dumped literally, --- which use |str2float()|. Strings are also dumped literally,
--- only single quote is escaped, which does not allow using YAML --- only single quote is escaped, which does not allow using YAML
--- for parsing back binary strings. |eval()| should always work for --- for parsing back binary strings. |eval()| should always work
--- strings and floats though and this is the only official --- for strings and floats though, and this is the only official
--- method, use |msgpackdump()| or |json_encode()| if you need to --- method. Use |msgpackdump()| or |json_encode()| if you need to
--- share data with other application. --- share data with other applications.
--- ---
--- @param expr any --- @param expr any
--- @return string --- @return string

View File

@ -969,7 +969,8 @@ 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 convert 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 convert)
@ -985,6 +986,8 @@ 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) {
return encode_tv2string(tv, NULL);
} }
return xstrdup(tv_get_string(tv)); return xstrdup(tv_get_string(tv));
} }

View File

@ -11129,10 +11129,10 @@ M.funcs = {
for infinite and NaN floating-point values representations for infinite and NaN floating-point values representations
which use |str2float()|. Strings are also dumped literally, which use |str2float()|. Strings are also dumped literally,
only single quote is escaped, which does not allow using YAML only single quote is escaped, which does not allow using YAML
for parsing back binary strings. |eval()| should always work for for parsing back binary strings. |eval()| should always work
strings and floats though and this is the only official for strings and floats though, and this is the only official
method, use |msgpackdump()| or |json_encode()| if you need to method. Use |msgpackdump()| or |json_encode()| if you need to
share data with other application. share data with other applications.
]=], ]=],
name = 'string', name = 'string',

View File

@ -52,43 +52,34 @@ describe('insert-mode', function()
end) end)
it('double quote is removed after hit-enter prompt #22609', function() it('double quote is removed after hit-enter prompt #22609', function()
local screen = Screen.new(60, 6) local screen = Screen.new(50, 6)
screen:set_default_attr_ids({
[0] = { bold = true, foreground = Screen.colors.Blue }, -- NonText
[1] = { foreground = Screen.colors.Blue }, -- SpecialKey
[2] = { foreground = Screen.colors.SlateBlue },
[3] = { bold = true }, -- ModeMsg
[4] = { reverse = true, bold = true }, -- MsgSeparator
[5] = { background = Screen.colors.Red, foreground = Screen.colors.White }, -- ErrorMsg
[6] = { foreground = Screen.colors.SeaGreen, bold = true }, -- MoreMsg
})
screen:attach() screen:attach()
feed('i<C-R>') feed('i<C-R>')
screen:expect([[ screen:expect([[
{1:^"} | {18:^"} |
{0:~ }|*4 {1:~ }|*4
{3:-- INSERT --} | {5:-- INSERT --} |
]]) ]])
feed('={}') feed("=function('add')")
screen:expect([[ screen:expect([[
{1:"} | {18:"} |
{0:~ }|*4 {1:~ }|*4
={2:{}}^ | ={25:function}{16:(}{26:'add'}{16:)}^ |
]]) ]])
feed('<CR>') feed('<CR>')
screen:expect([[ screen:expect([[
{1:"} | {18:"} |
{0:~ }| {1:~ }|
{4: }| {3: }|
={2:{}} | ={25:function}{16:(}{26:'add'}{16:)} |
{5:E731: Using a Dictionary as a String} | {9:E729: Using a Funcref as a String} |
{6:Press ENTER or type command to continue}^ | {6:Press ENTER or type command to continue}^ |
]]) ]])
feed('<CR>') feed('<CR>')
screen:expect([[ screen:expect([[
^ | ^ |
{0:~ }|*4 {1:~ }|*4
{3:-- INSERT --} | {5:-- INSERT --} |
]]) ]])
end) end)
end) end)

View File

@ -56,20 +56,20 @@ describe('edit', function()
{1:~ }|*4 {1:~ }|*4
{5:-- INSERT --} | {5:-- INSERT --} |
]]) ]])
feed('={}') feed('=0z')
screen:expect([[ screen:expect([[
{18:"} | {18:"} |
{1:~ }|*4 {1:~ }|*4
={16:{}}^ | ={26:0}{9:z}^ |
]]) ]])
-- trying to insert a dictionary produces an error -- trying to insert a blob produces an error
feed('<CR>') feed('<CR>')
screen:expect([[ screen:expect([[
{18:"} | {18:"} |
{1:~ }| {1:~ }|
{3: }| {3: }|
={16:{}} | ={26:0}{9:z} |
{9:E731: Using a Dictionary as a String} | {9:E976: Using a Blob as a String} |
{6:Press ENTER or type command to continue}^ | {6:Press ENTER or type command to continue}^ |
]]) ]])

View File

@ -1983,8 +1983,8 @@ func Test_edit_ctrl_r_failed()
let buf = RunVimInTerminal('', #{rows: 6, cols: 60}) let buf = RunVimInTerminal('', #{rows: 6, cols: 60})
" trying to insert a dictionary produces an error " trying to insert a blob produces an error
call term_sendkeys(buf, "i\<C-R>={}\<CR>") call term_sendkeys(buf, "i\<C-R>=0z\<CR>")
" ending Insert mode should put the cursor back on the ':' " ending Insert mode should put the cursor back on the ':'
call term_sendkeys(buf, ":\<Esc>") call term_sendkeys(buf, ":\<Esc>")

View File

@ -904,6 +904,10 @@ func Test_string_interp()
endif endif
call assert_equal(0, tmp) call assert_equal(0, tmp)
#" Dict interpolation
VAR d = {'a': 10, 'b': [1, 2]}
call assert_equal("{'a': 10, 'b': [1, 2]}", $'{d}')
#" Stray closing brace. #" Stray closing brace.
call assert_fails('echo $"moo}"', 'E1278:') call assert_fails('echo $"moo}"', 'E1278:')
#" Undefined variable in expansion. #" Undefined variable in expansion.

View File

@ -687,6 +687,13 @@ END
END END
call assert_equal(['let a = {abc}', 'let b = X', 'let c = {'], code) call assert_equal(['let a = {abc}', 'let b = X', 'let c = {'], code)
" Evaluate a dictionary
let d1 = #{a: 10, b: 'ss', c: {}}
let code =<< eval trim END
let d2 = {d1}
END
call assert_equal(["let d2 = {'a': 10, 'b': 'ss', 'c': {}}"], 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

@ -323,4 +323,13 @@ func Test_put_visual_replace_fold_marker()
bwipe! bwipe!
endfunc endfunc
func Test_put_dict()
new
let d = #{a: #{b: 'abc'}, c: [1, 2], d: 0z10}
put! =d
call assert_equal(["{'a': {'b': 'abc'}, 'c': [1, 2], 'd': 0z10}", ''],
\ getline(1, '$'))
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab