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:
150
src/ex_docmd.c
150
src/ex_docmd.c
@@ -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
|
||||||
|
|
||||||
|
@@ -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(['¬ex += 3'], 'E113:')
|
call CheckDefFailure(['¬ex += 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)
|
||||||
|
@@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user