0
0
mirror of https://github.com/vim/vim.git synced 2025-09-24 03:44:06 -04:00

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.
This commit is contained in:
Bram Moolenaar
2020-07-28 22:38:37 +02:00
parent 066b12e36c
commit b5ed266037
3 changed files with 109 additions and 68 deletions

View File

@@ -1710,7 +1710,7 @@ do_one_cmd(
char_u *cmd; char_u *cmd;
int starts_with_colon = FALSE; int starts_with_colon = FALSE;
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
int starts_with_quote; int may_have_range;
int vim9script = in_vim9script(); int vim9script = in_vim9script();
#endif #endif
@@ -1773,8 +1773,9 @@ do_one_cmd(
*/ */
cmd = ea.cmd; cmd = ea.cmd;
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
starts_with_quote = vim9script && !starts_with_colon && *ea.cmd == '\''; // In Vim9 script a colon is required before the range.
if (!starts_with_quote) may_have_range = !vim9script || starts_with_colon;
if (may_have_range)
#endif #endif
ea.cmd = skip_range(ea.cmd, NULL); ea.cmd = skip_range(ea.cmd, NULL);
if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL)
@@ -1783,7 +1784,10 @@ do_one_cmd(
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
if (vim9script && !starts_with_colon) if (vim9script && !starts_with_colon)
{ {
if (ea.cmd > cmd) if (ea.cmd == cmd + 1 && *cmd == '$')
// should be "$VAR = val"
--ea.cmd;
else if (ea.cmd > cmd)
{ {
emsg(_(e_colon_required)); emsg(_(e_colon_required));
goto doend; goto doend;
@@ -1876,7 +1880,7 @@ do_one_cmd(
ea.cmd = cmd; ea.cmd = cmd;
#ifdef FEAT_EVAL #ifdef FEAT_EVAL
if (!starts_with_quote) if (may_have_range)
#endif #endif
if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL) if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL)
goto doend; goto doend;
@@ -3267,81 +3271,91 @@ find_ex_command(
* "lvar = value", "lvar(arg)", "[1, 2 3]->Func()" * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()"
*/ */
p = eap->cmd; p = eap->cmd;
if (lookup != NULL && (vim_strchr((char_u *)"{('[", *p) != NULL if (lookup != NULL)
|| ((p = to_name_const_end(eap->cmd)) > eap->cmd
&& *p != NUL)))
{ {
int oplen; // Skip over first char for "&opt = val", "$ENV = val" and "@r = val".
int heredoc; char_u *pskip = (*eap->cmd == '&' || *eap->cmd == '$'
|| *eap->cmd == '@') ? eap->cmd + 1 : eap->cmd;
if ( if (vim_strchr((char_u *)"{('[", *p) != NULL
// "(..." is an expression. || ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL))
// "funcname(" is always a function call.
*p == '('
|| (p == eap->cmd
? (
// "{..." is an dict expression.
*eap->cmd == '{'
// "'string'->func()" is an expression.
|| *eap->cmd == '\''
// "g:varname" is an expression.
|| eap->cmd[1] == ':'
)
: (
// "varname[]" is an expression.
*p == '['
// "varname->func()" is an expression.
|| (*p == '-' && p[1] == '>')
// "varname.expr" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))
)))
{ {
eap->cmdidx = CMD_eval; int oplen;
return eap->cmd; int heredoc;
}
// "[...]->Method()" is a list expression, but "[a, b] = Func()" is if (
// an assignment. // "(..." is an expression.
// If there is no line break inside the "[...]" then "p" is advanced to // "funcname(" is always a function call.
// after the "]" by to_name_const_end(): check if a "=" follows. *p == '('
// If "[...]" has a line break "p" still points at the "[" and it can't || (p == eap->cmd
// be an assignment. ? (
if (*eap->cmd == '[') // "{..." is an dict expression.
{ *eap->cmd == '{'
p = to_name_const_end(eap->cmd); // "'string'->func()" is an expression.
if (p == eap->cmd || *skipwhite(p) != '=') || *eap->cmd == '\''
// "g:varname" is an expression.
|| eap->cmd[1] == ':'
)
: (
// "varname[]" is an expression.
*p == '['
// "varname->func()" is an expression.
|| (*p == '-' && p[1] == '>')
// "varname.expr" is an expression.
|| (*p == '.' && ASCII_ISALPHA(p[1]))
)))
{ {
eap->cmdidx = CMD_eval; eap->cmdidx = CMD_eval;
return eap->cmd; return eap->cmd;
} }
if (p > eap->cmd && *skipwhite(p) == '=')
// "[...]->Method()" is a list expression, but "[a, b] = Func()" is
// an assignment.
// If there is no line break inside the "[...]" then "p" is
// advanced to after the "]" by to_name_const_end(): check if a "="
// follows.
// If "[...]" has a line break "p" still points at the "[" and it
// can't be an assignment.
if (*eap->cmd == '[')
{ {
eap->cmdidx = CMD_let; p = to_name_const_end(eap->cmd);
if (p == eap->cmd || *skipwhite(p) != '=')
{
eap->cmdidx = CMD_eval;
return eap->cmd;
}
if (p > eap->cmd && *skipwhite(p) == '=')
{
eap->cmdidx = CMD_let;
return eap->cmd;
}
}
// Recognize an assignment if we recognize the variable name:
// "g:var = expr"
// "var = expr" where "var" is a local var name.
oplen = assignment_len(skipwhite(p), &heredoc);
if (oplen > 0)
{
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
|| *eap->cmd == '&'
|| *eap->cmd == '$'
|| *eap->cmd == '@'
|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
{
eap->cmdidx = CMD_let;
return eap->cmd;
}
}
// Recognize using a type for a w:, b:, t: or g: variable:
// "w:varname: number = 123".
if (eap->cmd[1] == ':' && *p == ':')
{
eap->cmdidx = CMD_eval;
return eap->cmd; return eap->cmd;
} }
} }
// Recognize an assignment if we recognize the variable name:
// "g:var = expr"
// "var = expr" where "var" is a local var name.
oplen = assignment_len(skipwhite(p), &heredoc);
if (oplen > 0)
{
if (((p - eap->cmd) > 2 && eap->cmd[1] == ':')
|| lookup(eap->cmd, p - eap->cmd, cctx) != NULL)
{
eap->cmdidx = CMD_let;
return eap->cmd;
}
}
// Recognize using a type for a w:, b:, t: or g: variable:
// "w:varname: number = 123".
if (eap->cmd[1] == ':' && *p == ':')
{
eap->cmdidx = CMD_eval;
return eap->cmd;
}
} }
#endif #endif

View File

@@ -61,6 +61,14 @@ def Test_assignment()
assert_equal('foobar', $ENVVAR) assert_equal('foobar', $ENVVAR)
$ENVVAR = '' $ENVVAR = ''
let lines =<< trim END
vim9script
$ENVVAR = 'barfoo'
assert_equal('barfoo', $ENVVAR)
$ENVVAR = ''
END
call CheckScriptSuccess(lines)
s:appendToMe ..= 'yyy' s:appendToMe ..= 'yyy'
assert_equal('xxxyyy', s:appendToMe) assert_equal('xxxyyy', s:appendToMe)
s:addToMe += 222 s:addToMe += 222
@@ -80,6 +88,15 @@ def Test_assignment()
set ts=10 set ts=10
&ts %= 4 &ts %= 4
assert_equal(2, &ts) assert_equal(2, &ts)
lines =<< trim END
vim9script
&ts = 6
&ts += 3
assert_equal(9, &ts)
END
call CheckScriptSuccess(lines)
call CheckDefFailure(['&notex += 3'], 'E113:') call CheckDefFailure(['&notex += 3'], 'E113:')
call CheckDefFailure(['&ts ..= "xxx"'], 'E1019:') call CheckDefFailure(['&ts ..= "xxx"'], 'E1019:')
call CheckDefFailure(['&ts = [7]'], 'E1013:') call CheckDefFailure(['&ts = [7]'], 'E1013:')
@@ -106,6 +123,14 @@ def Test_assignment()
call CheckDefFailure(['@a += "more"'], 'E1013:') call CheckDefFailure(['@a += "more"'], 'E1013:')
call CheckDefFailure(['@a += 123'], 'E1013:') call CheckDefFailure(['@a += 123'], 'E1013:')
lines =<< trim END
vim9script
@c = 'areg'
@c ..= 'add'
assert_equal('aregadd', @c)
END
call CheckScriptSuccess(lines)
v:errmsg = 'none' v:errmsg = 'none'
v:errmsg ..= 'again' v:errmsg ..= 'again'
assert_equal('noneagain', v:errmsg) assert_equal('noneagain', v:errmsg)

View File

@@ -754,6 +754,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 */
/**/
1313,
/**/ /**/
1312, 1312,
/**/ /**/