Skip to content

Commit b5ed266

Browse files
committed
patch 8.2.1313: Vim9 script: cannot assign to environment variable
Problem: Vim9 script: cannot assign to environment variable. Solution: Recognize environment variable assignment. (closes #6548) Also options and registers.
1 parent 066b12e commit b5ed266

3 files changed

Lines changed: 108 additions & 67 deletions

File tree

src/ex_docmd.c

Lines changed: 81 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1710,7 +1710,7 @@ do_one_cmd(
17101710
char_u *cmd;
17111711
int starts_with_colon = FALSE;
17121712
#ifdef FEAT_EVAL
1713-
int starts_with_quote;
1713+
int may_have_range;
17141714
int vim9script = in_vim9script();
17151715
#endif
17161716

@@ -1773,8 +1773,9 @@ do_one_cmd(
17731773
*/
17741774
cmd = ea.cmd;
17751775
#ifdef FEAT_EVAL
1776-
starts_with_quote = vim9script && !starts_with_colon && *ea.cmd == '\'';
1777-
if (!starts_with_quote)
1776+
// In Vim9 script a colon is required before the range.
1777+
may_have_range = !vim9script || starts_with_colon;
1778+
if (may_have_range)
17781779
#endif
17791780
ea.cmd = skip_range(ea.cmd, NULL);
17801781
if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
@@ -1783,7 +1784,10 @@ do_one_cmd(
17831784
#ifdef FEAT_EVAL
17841785
if (vim9script && !starts_with_colon)
17851786
{
1786-
if (ea.cmd > cmd)
1787+
if (ea.cmd == cmd + 1 && *cmd == '$')
1788+
// should be "$VAR = val"
1789+
--ea.cmd;
1790+
else if (ea.cmd > cmd)
17871791
{
17881792
emsg(_(e_colon_required));
17891793
goto doend;
@@ -1876,7 +1880,7 @@ do_one_cmd(
18761880

18771881
ea.cmd = cmd;
18781882
#ifdef FEAT_EVAL
1879-
if (!starts_with_quote)
1883+
if (may_have_range)
18801884
#endif
18811885
if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
18821886
goto doend;
@@ -3267,80 +3271,90 @@ find_ex_command(
32673271
* "lvar = value", "lvar(arg)", "[1, 2 3]->Func()"
32683272
*/
32693273
p = eap->cmd;
3270-
if (lookup != NULL && (vim_strchr((char_u *)"{('[", *p) != NULL
3271-
|| ((p = to_name_const_end(eap->cmd)) > eap->cmd
3272-
&& *p != NUL)))
3274+
if (lookup != NULL)
32733275
{
3274-
int oplen;
3275-
int heredoc;
3276+
// Skip over first char for "&opt = val", "$ENV = val" and "@r = val".
3277+
char_u *pskip = (*eap->cmd == '&' || *eap->cmd == '$'
3278+
|| *eap->cmd == '@') ? eap->cmd + 1 : eap->cmd;
32763279

3277-
if (
3278-
// "(..." is an expression.
3279-
// "funcname(" is always a function call.
3280-
*p == '('
3281-
|| (p == eap->cmd
3282-
? (
3283-
// "{..." is an dict expression.
3284-
*eap->cmd == '{'
3285-
// "'string'->func()" is an expression.
3286-
|| *eap->cmd == '\''
3287-
// "g:varname" is an expression.
3288-
|| eap->cmd[1] == ':'
3289-
)
3290-
: (
3291-
// "varname[]" is an expression.
3292-
*p == '['
3293-
// "varname->func()" is an expression.
3294-
|| (*p == '-' && p[1] == '>')
3295-
// "varname.expr" is an expression.
3296-
|| (*p == '.' && ASCII_ISALPHA(p[1]))
3297-
)))
3298-
{
3299-
eap->cmdidx = CMD_eval;
3300-
return eap->cmd;
3301-
}
3302-
3303-
// "[...]->Method()" is a list expression, but "[a, b] = Func()" is
3304-
// an assignment.
3305-
// If there is no line break inside the "[...]" then "p" is advanced to
3306-
// after the "]" by to_name_const_end(): check if a "=" follows.
3307-
// If "[...]" has a line break "p" still points at the "[" and it can't
3308-
// be an assignment.
3309-
if (*eap->cmd == '[')
3310-
{
3311-
p = to_name_const_end(eap->cmd);
3312-
if (p == eap->cmd || *skipwhite(p) != '=')
3280+
if (vim_strchr((char_u *)"{('[", *p) != NULL
3281+
|| ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
3282+
{
3283+
int oplen;
3284+
int heredoc;
3285+
3286+
if (
3287+
// "(..." is an expression.
3288+
// "funcname(" is always a function call.
3289+
*p == '('
3290+
|| (p == eap->cmd
3291+
? (
3292+
// "{..." is an dict expression.
3293+
*eap->cmd == '{'
3294+
// "'string'->func()" is an expression.
3295+
|| *eap->cmd == '\''
3296+
// "g:varname" is an expression.
3297+
|| eap->cmd[1] == ':'
3298+
)
3299+
: (
3300+
// "varname[]" is an expression.
3301+
*p == '['
3302+
// "varname->func()" is an expression.
3303+
|| (*p == '-' && p[1] == '>')
3304+
// "varname.expr" is an expression.
3305+
|| (*p == '.' && ASCII_ISALPHA(p[1]))
3306+
)))
33133307
{
33143308
eap->cmdidx = CMD_eval;
33153309
return eap->cmd;
33163310
}
3317-
if (p > eap->cmd && *skipwhite(p) == '=')
3311+
3312+
// "[...]->Method()" is a list expression, but "[a, b] = Func()" is
3313+
// an assignment.
3314+
// If there is no line break inside the "[...]" then "p" is
3315+
// advanced to after the "]" by to_name_const_end(): check if a "="
3316+
// follows.
3317+
// If "[...]" has a line break "p" still points at the "[" and it
3318+
// can't be an assignment.
3319+
if (*eap->cmd == '[')
33183320
{
3319-
eap->cmdidx = CMD_let;
3320-
return eap->cmd;
3321+
p = to_name_const_end(eap->cmd);
3322+
if (p == eap->cmd || *skipwhite(p) != '=')
3323+
{
3324+
eap->cmdidx = CMD_eval;
3325+
return eap->cmd;
3326+
}
3327+
if (p > eap->cmd && *skipwhite(p) == '=')
3328+
{
3329+
eap->cmdidx = CMD_let;
3330+
return eap->cmd;
3331+
}
33213332
}
3322-
}
33233333

3324-
// Recognize an assignment if we recognize the variable name:
3325-
// "g:var = expr"
3326-
// "var = expr" where "var" is a local var name.
3327-
oplen = assignment_len(skipwhite(p), &heredoc);
3328-
if (oplen > 0)
3329-
{
3330-
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
3331-
|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
3334+
// Recognize an assignment if we recognize the variable name:
3335+
// "g:var = expr"
3336+
// "var = expr" where "var" is a local var name.
3337+
oplen = assignment_len(skipwhite(p), &heredoc);
3338+
if (oplen > 0)
33323339
{
3333-
eap->cmdidx = CMD_let;
3334-
return eap->cmd;
3340+
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
3341+
|| *eap->cmd == '&'
3342+
|| *eap->cmd == '$'
3343+
|| *eap->cmd == '@'
3344+
|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
3345+
{
3346+
eap->cmdidx = CMD_let;
3347+
return eap->cmd;
3348+
}
33353349
}
3336-
}
33373350

3338-
// Recognize using a type for a w:, b:, t: or g: variable:
3339-
// "w:varname: number = 123".
3340-
if (eap->cmd[1] == ':' && *p == ':')
3341-
{
3342-
eap->cmdidx = CMD_eval;
3343-
return eap->cmd;
3351+
// Recognize using a type for a w:, b:, t: or g: variable:
3352+
// "w:varname: number = 123".
3353+
if (eap->cmd[1] == ':' && *p == ':')
3354+
{
3355+
eap->cmdidx = CMD_eval;
3356+
return eap->cmd;
3357+
}
33443358
}
33453359
}
33463360
#endif

src/testdir/test_vim9_script.vim

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,14 @@ def Test_assignment()
6161
assert_equal('foobar', $ENVVAR)
6262
$ENVVAR = ''
6363

64+
let lines =<< trim END
65+
vim9script
66+
$ENVVAR = 'barfoo'
67+
assert_equal('barfoo', $ENVVAR)
68+
$ENVVAR = ''
69+
END
70+
call CheckScriptSuccess(lines)
71+
6472
s:appendToMe ..= 'yyy'
6573
assert_equal('xxxyyy', s:appendToMe)
6674
s:addToMe += 222
@@ -80,6 +88,15 @@ def Test_assignment()
8088
set ts=10
8189
&ts %= 4
8290
assert_equal(2, &ts)
91+
92+
lines =<< trim END
93+
vim9script
94+
&ts = 6
95+
&ts += 3
96+
assert_equal(9, &ts)
97+
END
98+
call CheckScriptSuccess(lines)
99+
83100
call CheckDefFailure(['&notex += 3'], 'E113:')
84101
call CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
85102
call CheckDefFailure(['&ts = [7]'], 'E1013:')
@@ -106,6 +123,14 @@ def Test_assignment()
106123
call CheckDefFailure(['@a += "more"'], 'E1013:')
107124
call CheckDefFailure(['@a += 123'], 'E1013:')
108125

126+
lines =<< trim END
127+
vim9script
128+
@c = 'areg'
129+
@c ..= 'add'
130+
assert_equal('aregadd', @c)
131+
END
132+
call CheckScriptSuccess(lines)
133+
109134
v:errmsg = 'none'
110135
v:errmsg ..= 'again'
111136
assert_equal('noneagain', v:errmsg)

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,8 @@ static char *(features[]) =
754754

755755
static int included_patches[] =
756756
{ /* Add new patch number below this line */
757+
/**/
758+
1313,
757759
/**/
758760
1312,
759761
/**/

0 commit comments

Comments
 (0)