To: vim_dev@googlegroups.com Subject: Patch 8.2.1313 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1313 Problem: Vim9 script: cannot assign to environment variable. Solution: Recognize environment variable assignment. (closes #6548) Also options and registers. Files: src/ex_docmd.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1312/src/ex_docmd.c 2020-07-28 20:25:43.830309906 +0200 --- src/ex_docmd.c 2020-07-28 22:38:14.428582254 +0200 *************** *** 1710,1716 **** char_u *cmd; int starts_with_colon = FALSE; #ifdef FEAT_EVAL ! int starts_with_quote; int vim9script = in_vim9script(); #endif --- 1710,1716 ---- char_u *cmd; int starts_with_colon = FALSE; #ifdef FEAT_EVAL ! int may_have_range; int vim9script = in_vim9script(); #endif *************** *** 1773,1780 **** */ cmd = ea.cmd; #ifdef FEAT_EVAL ! starts_with_quote = vim9script && !starts_with_colon && *ea.cmd == '\''; ! if (!starts_with_quote) #endif ea.cmd = skip_range(ea.cmd, NULL); if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) --- 1773,1781 ---- */ cmd = ea.cmd; #ifdef FEAT_EVAL ! // In Vim9 script a colon is required before the range. ! may_have_range = !vim9script || starts_with_colon; ! if (may_have_range) #endif ea.cmd = skip_range(ea.cmd, NULL); if (*ea.cmd == '*' && vim_strchr(p_cpo, CPO_STAR) == NULL) *************** *** 1783,1789 **** #ifdef FEAT_EVAL if (vim9script && !starts_with_colon) { ! if (ea.cmd > cmd) { emsg(_(e_colon_required)); goto doend; --- 1784,1793 ---- #ifdef FEAT_EVAL if (vim9script && !starts_with_colon) { ! if (ea.cmd == cmd + 1 && *cmd == '$') ! // should be "$VAR = val" ! --ea.cmd; ! else if (ea.cmd > cmd) { emsg(_(e_colon_required)); goto doend; *************** *** 1876,1882 **** ea.cmd = cmd; #ifdef FEAT_EVAL ! if (!starts_with_quote) #endif if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL) goto doend; --- 1880,1886 ---- ea.cmd = cmd; #ifdef FEAT_EVAL ! if (may_have_range) #endif if (parse_cmd_address(&ea, &errormsg, FALSE) == FAIL) goto doend; *************** *** 3267,3346 **** * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()" */ p = eap->cmd; ! if (lookup != NULL && (vim_strchr((char_u *)"{('[", *p) != NULL ! || ((p = to_name_const_end(eap->cmd)) > eap->cmd ! && *p != NUL))) ! { ! int oplen; ! int heredoc; ! ! if ( ! // "(..." is an expression. ! // "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; ! return eap->cmd; ! } ! ! // "[...]->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 == '[') ! { ! 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] == ':') ! || 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 --- 3271,3360 ---- * "lvar = value", "lvar(arg)", "[1, 2 3]->Func()" */ p = eap->cmd; ! if (lookup != NULL) ! { ! // Skip over first char for "&opt = val", "$ENV = val" and "@r = val". ! char_u *pskip = (*eap->cmd == '&' || *eap->cmd == '$' ! || *eap->cmd == '@') ? eap->cmd + 1 : eap->cmd; ! ! if (vim_strchr((char_u *)"{('[", *p) != NULL ! || ((p = to_name_const_end(pskip)) > eap->cmd && *p != NUL)) ! { ! int oplen; ! int heredoc; ! ! if ( ! // "(..." is an expression. ! // "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; return eap->cmd; } ! ! // "[...]->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 == '[') { ! 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; ! } } } #endif *** ../vim-8.2.1312/src/testdir/test_vim9_script.vim 2020-07-28 20:06:46.115280293 +0200 --- src/testdir/test_vim9_script.vim 2020-07-28 22:26:40.475600582 +0200 *************** *** 61,66 **** --- 61,74 ---- assert_equal('foobar', $ENVVAR) $ENVVAR = '' + let lines =<< trim END + vim9script + $ENVVAR = 'barfoo' + assert_equal('barfoo', $ENVVAR) + $ENVVAR = '' + END + call CheckScriptSuccess(lines) + s:appendToMe ..= 'yyy' assert_equal('xxxyyy', s:appendToMe) s:addToMe += 222 *************** *** 80,85 **** --- 88,102 ---- set ts=10 &ts %= 4 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(['&ts ..= "xxx"'], 'E1019:') call CheckDefFailure(['&ts = [7]'], 'E1013:') *************** *** 106,111 **** --- 123,136 ---- call CheckDefFailure(['@a += "more"'], '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 ..= 'again' assert_equal('noneagain', v:errmsg) *** ../vim-8.2.1312/src/version.c 2020-07-28 21:40:23.708223773 +0200 --- src/version.c 2020-07-28 22:37:41.444715791 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1313, /**/ -- % cat /usr/include/long_life.h long life(double fun); /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///