mirror of
https://github.com/vim/vim.git
synced 2025-07-26 11:04:33 -04:00
patch 8.2.2015: Vim9: literal dict #{} is not like any other language
Problem: Vim9: literal dict #{} is not like any other language. Solution: Support the JavaScript syntax.
This commit is contained in:
parent
ee8b787bcd
commit
2bede173a1
@ -112,8 +112,7 @@ In Vi # is a command to list text with numbers. In Vim9 script you can use
|
|||||||
101 number
|
101 number
|
||||||
|
|
||||||
To improve readability there must be a space between a command and the #
|
To improve readability there must be a space between a command and the #
|
||||||
that starts a comment. Note that #{ is the start of a dictionary, therefore
|
that starts a comment.
|
||||||
it does not start a comment.
|
|
||||||
|
|
||||||
|
|
||||||
Vim9 functions ~
|
Vim9 functions ~
|
||||||
@ -303,8 +302,7 @@ identifier or can't be an Ex command. Examples: >
|
|||||||
myList->add(123)
|
myList->add(123)
|
||||||
g:myList->add(123)
|
g:myList->add(123)
|
||||||
[1, 2, 3]->Process()
|
[1, 2, 3]->Process()
|
||||||
#{a: 1, b: 2}->Process()
|
{a: 1, b: 2}->Process()
|
||||||
{'a': 1, 'b': 2}->Process()
|
|
||||||
"foobar"->Process()
|
"foobar"->Process()
|
||||||
("foobar")->Process()
|
("foobar")->Process()
|
||||||
'foobar'->Process()
|
'foobar'->Process()
|
||||||
@ -346,7 +344,7 @@ those cases there is no need to prefix the line with a backslash
|
|||||||
'two',
|
'two',
|
||||||
]
|
]
|
||||||
And when a dict spans multiple lines: >
|
And when a dict spans multiple lines: >
|
||||||
var mydict = #{
|
var mydict = {
|
||||||
one: 1,
|
one: 1,
|
||||||
two: 2,
|
two: 2,
|
||||||
}
|
}
|
||||||
@ -430,6 +428,27 @@ No curly braces expansion ~
|
|||||||
|curly-braces-names| cannot be used.
|
|curly-braces-names| cannot be used.
|
||||||
|
|
||||||
|
|
||||||
|
Dictionary literals ~
|
||||||
|
|
||||||
|
Traditionally Vim has supported dictionary literals with a {} syntax: >
|
||||||
|
let dict = {'key': value}
|
||||||
|
|
||||||
|
Later it became clear that using a simple key name is very common, thus
|
||||||
|
literally dictionaries were introduced in a backwards compatible way: >
|
||||||
|
let dict = #{key: value}
|
||||||
|
|
||||||
|
However, this #{} syntax is unlike any existing language. As it appears that
|
||||||
|
using a literaly key is much more common than using an expression, and
|
||||||
|
considering that JavaScript uses this syntax, using the {} form for dictionary
|
||||||
|
literals was considered a much more useful syntax. In Vim9 script the {} form
|
||||||
|
uses literal keys: >
|
||||||
|
let dict = {key: value}
|
||||||
|
|
||||||
|
In case an expression needs to be used for the key, square brackets can be
|
||||||
|
used, just like in JavaScript: >
|
||||||
|
let dict = {["key" .. nr]: value}
|
||||||
|
|
||||||
|
|
||||||
No :xit, :t, :append, :change or :insert ~
|
No :xit, :t, :append, :change or :insert ~
|
||||||
|
|
||||||
These commands are too easily confused with local variable names.
|
These commands are too easily confused with local variable names.
|
||||||
|
@ -303,3 +303,5 @@ EXTERN char e_cmd_maping_must_not_include_str_key[]
|
|||||||
INIT(= N_("E1137: <Cmd> mapping must not include %s key"));
|
INIT(= N_("E1137: <Cmd> mapping must not include %s key"));
|
||||||
EXTERN char e_using_bool_as_number[]
|
EXTERN char e_using_bool_as_number[]
|
||||||
INIT(= N_("E1138: Using a Bool as a Number"));
|
INIT(= N_("E1138: Using a Bool as a Number"));
|
||||||
|
EXTERN char e_missing_matching_bracket_after_dict_key[]
|
||||||
|
INIT(= N_("E1139: Missing matching bracket after dict key"));
|
||||||
|
@ -8,6 +8,7 @@ imported_T *find_imported_in_script(char_u *name, size_t len, int sid);
|
|||||||
int vim9_comment_start(char_u *p);
|
int vim9_comment_start(char_u *p);
|
||||||
char_u *peek_next_line_from_context(cctx_T *cctx);
|
char_u *peek_next_line_from_context(cctx_T *cctx);
|
||||||
char_u *next_line_from_context(cctx_T *cctx, int skip_comment);
|
char_u *next_line_from_context(cctx_T *cctx, int skip_comment);
|
||||||
|
char_u *to_name_end(char_u *arg, int namespace);
|
||||||
char_u *to_name_const_end(char_u *arg);
|
char_u *to_name_const_end(char_u *arg);
|
||||||
exptype_T get_compare_type(char_u *p, int *len, int *type_is);
|
exptype_T get_compare_type(char_u *p, int *len, int *type_is);
|
||||||
void error_white_both(char_u *op, int len);
|
void error_white_both(char_u *op, int len);
|
||||||
|
@ -226,7 +226,7 @@ enddef
|
|||||||
|
|
||||||
|
|
||||||
def Wrong_dict_key_type(items: list<number>): list<number>
|
def Wrong_dict_key_type(items: list<number>): list<number>
|
||||||
return filter(items, {_, val -> get({val: 1}, 'x')})
|
return filter(items, {_, val -> get({[val]: 1}, 'x')})
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
def Test_filter_wrong_dict_key_type()
|
def Test_filter_wrong_dict_key_type()
|
||||||
|
@ -1883,6 +1883,9 @@ def Test_epxr7_funcref()
|
|||||||
CheckDefAndScriptSuccess(lines)
|
CheckDefAndScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
let g:test_space_dict = {'': 'empty', ' ': 'space'}
|
||||||
|
let g:test_hash_dict = #{one: 1, two: 2}
|
||||||
|
|
||||||
def Test_expr7_dict()
|
def Test_expr7_dict()
|
||||||
# dictionary
|
# dictionary
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
@ -1891,17 +1894,17 @@ def Test_expr7_dict()
|
|||||||
assert_equal(g:dict_one, {'one': 1})
|
assert_equal(g:dict_one, {'one': 1})
|
||||||
var key = 'one'
|
var key = 'one'
|
||||||
var val = 1
|
var val = 1
|
||||||
assert_equal(g:dict_one, {key: val})
|
assert_equal(g:dict_one, {[key]: val})
|
||||||
|
|
||||||
var numbers: dict<number> = #{a: 1, b: 2, c: 3}
|
var numbers: dict<number> = {a: 1, b: 2, c: 3}
|
||||||
numbers = #{a: 1}
|
numbers = #{a: 1}
|
||||||
numbers = #{}
|
numbers = #{}
|
||||||
|
|
||||||
var strings: dict<string> = #{a: 'a', b: 'b', c: 'c'}
|
var strings: dict<string> = {a: 'a', b: 'b', c: 'c'}
|
||||||
strings = #{a: 'x'}
|
strings = #{a: 'x'}
|
||||||
strings = #{}
|
strings = #{}
|
||||||
|
|
||||||
var mixed: dict<any> = #{a: 'a', b: 42}
|
var mixed: dict<any> = {a: 'a', b: 42}
|
||||||
mixed = #{a: 'x'}
|
mixed = #{a: 'x'}
|
||||||
mixed = #{a: 234}
|
mixed = #{a: 234}
|
||||||
mixed = #{}
|
mixed = #{}
|
||||||
@ -1915,6 +1918,9 @@ def Test_expr7_dict()
|
|||||||
dictdict = #{one: #{}, two: #{}}
|
dictdict = #{one: #{}, two: #{}}
|
||||||
|
|
||||||
assert_equal({'': 0}, {matchstr('string', 'wont match'): 0})
|
assert_equal({'': 0}, {matchstr('string', 'wont match'): 0})
|
||||||
|
|
||||||
|
assert_equal(g:test_space_dict, {['']: 'empty', [' ']: 'space'})
|
||||||
|
assert_equal(g:test_hash_dict, {one: 1, two: 2})
|
||||||
END
|
END
|
||||||
CheckDefAndScriptSuccess(lines)
|
CheckDefAndScriptSuccess(lines)
|
||||||
|
|
||||||
@ -1929,7 +1935,7 @@ def Test_expr7_dict()
|
|||||||
CheckDefFailure(["var x = #{xxx: 1", "var y = 2"], 'E722:', 2)
|
CheckDefFailure(["var x = #{xxx: 1", "var y = 2"], 'E722:', 2)
|
||||||
CheckDefFailure(["var x = #{xxx: 1,"], 'E723:', 2)
|
CheckDefFailure(["var x = #{xxx: 1,"], 'E723:', 2)
|
||||||
CheckDefFailure(["var x = {'a': xxx}"], 'E1001:', 1)
|
CheckDefFailure(["var x = {'a': xxx}"], 'E1001:', 1)
|
||||||
CheckDefFailure(["var x = {xxx: 8}"], 'E1001:', 1)
|
CheckDefFailure(["var x = {xx-x: 8}"], 'E1001:', 1)
|
||||||
CheckDefFailure(["var x = #{a: 1, a: 2}"], 'E721:', 1)
|
CheckDefFailure(["var x = #{a: 1, a: 2}"], 'E721:', 1)
|
||||||
CheckDefFailure(["var x = #"], 'E1015:', 1)
|
CheckDefFailure(["var x = #"], 'E1015:', 1)
|
||||||
CheckDefExecFailure(["var x = g:anint.member"], 'E715:', 1)
|
CheckDefExecFailure(["var x = g:anint.member"], 'E715:', 1)
|
||||||
|
@ -1569,7 +1569,7 @@ enddef
|
|||||||
def TreeWalk(dir: string): list<any>
|
def TreeWalk(dir: string): list<any>
|
||||||
return readdir(dir)->map({_, val ->
|
return readdir(dir)->map({_, val ->
|
||||||
fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
|
fnamemodify(dir .. '/' .. val, ':p')->isdirectory()
|
||||||
? {val: TreeWalk(dir .. '/' .. val)}
|
? {[val]: TreeWalk(dir .. '/' .. val)}
|
||||||
: val
|
: val
|
||||||
})
|
})
|
||||||
enddef
|
enddef
|
||||||
|
@ -416,7 +416,7 @@ def Test_try_catch()
|
|||||||
|
|
||||||
var nd: dict<any>
|
var nd: dict<any>
|
||||||
try
|
try
|
||||||
nd = {g:anumber: 1}
|
nd = {[g:anumber]: 1}
|
||||||
catch /E1012:/
|
catch /E1012:/
|
||||||
n = 266
|
n = 266
|
||||||
endtry
|
endtry
|
||||||
@ -459,7 +459,7 @@ def Test_try_catch()
|
|||||||
assert_equal(322, n)
|
assert_equal(322, n)
|
||||||
|
|
||||||
try
|
try
|
||||||
d = {'text': 1, g:astring: 2}
|
d = {text: 1, [g:astring]: 2}
|
||||||
catch /E721:/
|
catch /E721:/
|
||||||
n = 333
|
n = 333
|
||||||
endtry
|
endtry
|
||||||
|
@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
2015,
|
||||||
/**/
|
/**/
|
||||||
2014,
|
2014,
|
||||||
/**/
|
/**/
|
||||||
|
@ -2771,7 +2771,7 @@ theend:
|
|||||||
* Return a pointer to just after the name. Equal to "arg" if there is no
|
* Return a pointer to just after the name. Equal to "arg" if there is no
|
||||||
* valid name.
|
* valid name.
|
||||||
*/
|
*/
|
||||||
static char_u *
|
char_u *
|
||||||
to_name_end(char_u *arg, int namespace)
|
to_name_end(char_u *arg, int namespace)
|
||||||
{
|
{
|
||||||
char_u *p;
|
char_u *p;
|
||||||
@ -2989,6 +2989,7 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal, ppconst_T *ppconst)
|
|||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
char_u *key = NULL;
|
char_u *key = NULL;
|
||||||
|
char_u *end;
|
||||||
|
|
||||||
if (may_get_next_line(whitep, arg, cctx) == FAIL)
|
if (may_get_next_line(whitep, arg, cctx) == FAIL)
|
||||||
{
|
{
|
||||||
@ -2999,10 +3000,14 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal, ppconst_T *ppconst)
|
|||||||
if (**arg == '}')
|
if (**arg == '}')
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (literal)
|
// Eventually {name: value} will use "name" as a literal key and
|
||||||
|
// {[expr]: value} for an evaluated key.
|
||||||
|
// Temporarily: if "name" is indeed a valid key, or "[expr]" is
|
||||||
|
// used, use the new method, like JavaScript. Otherwise fall back
|
||||||
|
// to the old method.
|
||||||
|
end = to_name_end(*arg, FALSE);
|
||||||
|
if (literal || *end == ':')
|
||||||
{
|
{
|
||||||
char_u *end = to_name_end(*arg, !literal);
|
|
||||||
|
|
||||||
if (end == *arg)
|
if (end == *arg)
|
||||||
{
|
{
|
||||||
semsg(_(e_invalid_key_str), *arg);
|
semsg(_(e_invalid_key_str), *arg);
|
||||||
@ -3016,7 +3021,10 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal, ppconst_T *ppconst)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
|
int has_bracket = **arg == '[';
|
||||||
|
|
||||||
|
if (has_bracket)
|
||||||
|
*arg = skipwhite(*arg + 1);
|
||||||
if (compile_expr0(arg, cctx) == FAIL)
|
if (compile_expr0(arg, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
|
isn = ((isn_T *)instr->ga_data) + instr->ga_len - 1;
|
||||||
@ -3030,6 +3038,16 @@ compile_dict(char_u **arg, cctx_T *cctx, int literal, ppconst_T *ppconst)
|
|||||||
FALSE, FALSE) == FAIL)
|
FALSE, FALSE) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
if (has_bracket)
|
||||||
|
{
|
||||||
|
*arg = skipwhite(*arg);
|
||||||
|
if (**arg != ']')
|
||||||
|
{
|
||||||
|
emsg(_(e_missing_matching_bracket_after_dict_key));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
++*arg;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duplicate keys, if using string keys.
|
// Check for duplicate keys, if using string keys.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user