To: vim_dev@googlegroups.com Subject: Patch 8.1.0902 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.0902 Problem: Incomplete set of assignment operators. Solution: Add /=, *= and %=. (Ozaki Kiichi, closes #3931) Files: runtime/doc/eval.txt src/eval.c src/testdir/test_vimscript.vim *** ../vim-8.1.0901/runtime/doc/eval.txt 2019-02-10 23:18:49.034187552 +0100 --- runtime/doc/eval.txt 2019-02-12 22:21:20.627600700 +0100 *************** *** 10774,10782 **** When the selected range of items is partly past the end of the list, items will be added. ! *:let+=* *:let-=* *:let.=* *E734* :let {var} += {expr1} Like ":let {var} = {var} + {expr1}". :let {var} -= {expr1} Like ":let {var} = {var} - {expr1}". :let {var} .= {expr1} Like ":let {var} = {var} . {expr1}". These fail if {var} was not set yet and when the type of {var} and {expr1} don't fit the operator. --- 10836,10848 ---- When the selected range of items is partly past the end of the list, items will be added. ! *:let+=* *:let-=* *:letstar=* ! *:let/=* *:let%=* *:let.=* *E734* :let {var} += {expr1} Like ":let {var} = {var} + {expr1}". :let {var} -= {expr1} Like ":let {var} = {var} - {expr1}". + :let {var} *= {expr1} Like ":let {var} = {var} * {expr1}". + :let {var} /= {expr1} Like ":let {var} = {var} / {expr1}". + :let {var} %= {expr1} Like ":let {var} = {var} % {expr1}". :let {var} .= {expr1} Like ":let {var} = {var} . {expr1}". These fail if {var} was not set yet and when the type of {var} and {expr1} don't fit the operator. *** ../vim-8.1.0901/src/eval.c 2019-02-12 20:48:06.646810620 +0100 --- src/eval.c 2019-02-12 22:25:01.466416885 +0100 *************** *** 1197,1202 **** --- 1197,1205 ---- * ":let var = expr" assignment command. * ":let var += expr" assignment command. * ":let var -= expr" assignment command. + * ":let var *= expr" assignment command. + * ":let var /= expr" assignment command. + * ":let var %= expr" assignment command. * ":let var .= expr" assignment command. * ":let [var1, var2] = expr" unpack list. */ *************** *** 1216,1225 **** argend = skip_var_list(arg, &var_count, &semicolon); if (argend == NULL) return; ! if (argend > arg && argend[-1] == '.') /* for var.='str' */ --argend; expr = skipwhite(argend); ! if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL && expr[1] == '=')) { /* --- 1219,1228 ---- argend = skip_var_list(arg, &var_count, &semicolon); if (argend == NULL) return; ! if (argend > arg && argend[-1] == '.') // for var.='str' --argend; expr = skipwhite(argend); ! if (*expr != '=' && !(vim_strchr((char_u *)"+-*/%.", *expr) != NULL && expr[1] == '=')) { /* *************** *** 1249,1256 **** op[1] = NUL; if (*expr != '=') { ! if (vim_strchr((char_u *)"+-.", *expr) != NULL) ! op[0] = *expr; /* +=, -= or .= */ expr = skipwhite(expr + 2); } else --- 1252,1259 ---- op[1] = NUL; if (*expr != '=') { ! if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL) ! op[0] = *expr; // +=, -=, *=, /=, %= or .= expr = skipwhite(expr + 2); } else *************** *** 1671,1677 **** semsg(_(e_invarg2), name - 1); else { ! if (op != NULL && (*op == '+' || *op == '-')) semsg(_(e_letwrong), op); else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg)) == NULL) --- 1674,1680 ---- semsg(_(e_invarg2), name - 1); else { ! if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) semsg(_(e_letwrong), op); else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg)) == NULL) *************** *** 1744,1761 **** || (opt_type == 0 && *op != '.')) { semsg(_(e_letwrong), op); ! s = NULL; /* don't set the value */ } else { ! if (opt_type == 1) /* number */ { ! if (*op == '+') ! n = numval + n; ! else ! n = numval - n; } ! else if (opt_type == 0 && stringval != NULL) /* string */ { s = concat_str(stringval, s); vim_free(stringval); --- 1747,1768 ---- || (opt_type == 0 && *op != '.')) { semsg(_(e_letwrong), op); ! s = NULL; // don't set the value } else { ! if (opt_type == 1) // number { ! switch (*op) ! { ! case '+': n = numval + n; break; ! case '-': n = numval - n; break; ! case '*': n = numval * n; break; ! case '/': n = numval / n; break; ! case '%': n = numval % n; break; ! } } ! else if (opt_type == 0 && stringval != NULL) // string { s = concat_str(stringval, s); vim_free(stringval); *************** *** 1779,1785 **** else if (*arg == '@') { ++arg; ! if (op != NULL && (*op == '+' || *op == '-')) semsg(_(e_letwrong), op); else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) --- 1786,1792 ---- else if (*arg == '@') { ++arg; ! if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) semsg(_(e_letwrong), op); else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) *************** *** 2254,2260 **** /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. ! * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=". */ static void set_var_lval( --- 2261,2268 ---- /* * Set a variable that was parsed by get_lval() to "rettv". * "endp" points to just after the parsed name. ! * "op" is NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", ! * "%" for "%=", "." for ".=" or "=" for "=". */ static void set_var_lval( *************** *** 2327,2333 **** { typval_T tv; ! /* handle +=, -= and .= */ di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, TRUE, FALSE) == OK) --- 2335,2341 ---- { typval_T tv; ! // handle +=, -=, *=, /=, %= and .= di = NULL; if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, TRUE, FALSE) == OK) *************** *** 2448,2454 **** } /* ! * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2" * Returns OK or FAIL. */ static int --- 2456,2463 ---- } /* ! * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2" ! * and "tv1 .= tv2" * Returns OK or FAIL. */ static int *************** *** 2490,2496 **** case VAR_LIST: if (*op != '+' || tv2->v_type != VAR_LIST) break; ! /* List += List */ if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); return OK; --- 2499,2505 ---- case VAR_LIST: if (*op != '+' || tv2->v_type != VAR_LIST) break; ! // List += List if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); return OK; *************** *** 2499,2517 **** case VAR_STRING: if (tv2->v_type == VAR_LIST) break; ! if (*op == '+' || *op == '-') { ! /* nr += nr or nr -= nr*/ n = tv_get_number(tv1); #ifdef FEAT_FLOAT if (tv2->v_type == VAR_FLOAT) { float_T f = n; ! if (*op == '+') ! f += tv2->vval.v_float; ! else ! f -= tv2->vval.v_float; clear_tv(tv1); tv1->v_type = VAR_FLOAT; tv1->vval.v_float = f; --- 2508,2531 ---- case VAR_STRING: if (tv2->v_type == VAR_LIST) break; ! if (vim_strchr((char_u *)"+-*/%", *op) != NULL) { ! // nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr n = tv_get_number(tv1); #ifdef FEAT_FLOAT if (tv2->v_type == VAR_FLOAT) { float_T f = n; ! if (*op == '%') ! break; ! switch (*op) ! { ! case '+': f += tv2->vval.v_float; break; ! case '-': f -= tv2->vval.v_float; break; ! case '*': f *= tv2->vval.v_float; break; ! case '/': f /= tv2->vval.v_float; break; ! } clear_tv(tv1); tv1->v_type = VAR_FLOAT; tv1->vval.v_float = f; *************** *** 2519,2528 **** else #endif { ! if (*op == '+') ! n += tv_get_number(tv2); ! else ! n -= tv_get_number(tv2); clear_tv(tv1); tv1->v_type = VAR_NUMBER; tv1->vval.v_number = n; --- 2533,2546 ---- else #endif { ! switch (*op) ! { ! case '+': n += tv_get_number(tv2); break; ! case '-': n -= tv_get_number(tv2); break; ! case '*': n *= tv_get_number(tv2); break; ! case '/': n /= tv_get_number(tv2); break; ! case '%': n %= tv_get_number(tv2); break; ! } clear_tv(tv1); tv1->v_type = VAR_NUMBER; tv1->vval.v_number = n; *************** *** 2533,2539 **** if (tv2->v_type == VAR_FLOAT) break; ! /* str .= str */ s = tv_get_string(tv1); s = concat_str(s, tv_get_string_buf(tv2, numbuf)); clear_tv(tv1); --- 2551,2557 ---- if (tv2->v_type == VAR_FLOAT) break; ! // str .= str s = tv_get_string(tv1); s = concat_str(s, tv_get_string_buf(tv2, numbuf)); clear_tv(tv1); *************** *** 2547,2553 **** { float_T f; ! if (*op == '.' || (tv2->v_type != VAR_FLOAT && tv2->v_type != VAR_NUMBER && tv2->v_type != VAR_STRING)) break; --- 2565,2572 ---- { float_T f; ! if (*op == '%' || *op == '.' ! || (tv2->v_type != VAR_FLOAT && tv2->v_type != VAR_NUMBER && tv2->v_type != VAR_STRING)) break; *************** *** 2555,2564 **** f = tv2->vval.v_float; else f = tv_get_number(tv2); ! if (*op == '+') ! tv1->vval.v_float += f; ! else ! tv1->vval.v_float -= f; } #endif return OK; --- 2574,2586 ---- f = tv2->vval.v_float; else f = tv_get_number(tv2); ! switch (*op) ! { ! case '+': tv1->vval.v_float += f; break; ! case '-': tv1->vval.v_float -= f; break; ! case '*': tv1->vval.v_float *= f; break; ! case '/': tv1->vval.v_float /= f; break; ! } } #endif return OK; *** ../vim-8.1.0901/src/testdir/test_vimscript.vim 2019-01-09 23:00:58.001176090 +0100 --- src/testdir/test_vimscript.vim 2019-02-12 22:21:20.627600700 +0100 *************** *** 1441,1446 **** --- 1441,1524 ---- enew! | close endfunc + func Test_compound_assignment_operators() + " Test for number + let x = 1 + let x += 10 + call assert_equal(11, x) + let x -= 5 + call assert_equal(6, x) + let x *= 4 + call assert_equal(24, x) + let x /= 3 + call assert_equal(8, x) + let x %= 3 + call assert_equal(2, x) + let x .= 'n' + call assert_equal('2n', x) + + " Test for string + let x = 'str' + let x .= 'ing' + call assert_equal('string', x) + let x += 1 + call assert_equal(1, x) + let x -= 1.5 + call assert_equal(-0.5, x) + + if has('float') + " Test for float + let x = 0.5 + let x += 4.5 + call assert_equal(5.0, x) + let x -= 1.5 + call assert_equal(3.5, x) + let x *= 3.0 + call assert_equal(10.5, x) + let x /= 2.5 + call assert_equal(4.2, x) + call assert_fails('let x %= 0.5', 'E734') + call assert_fails('let x .= "f"', 'E734') + endif + + " Test for environment variable + let $FOO = 1 + call assert_fails('let $FOO += 1', 'E734') + call assert_fails('let $FOO -= 1', 'E734') + call assert_fails('let $FOO *= 1', 'E734') + call assert_fails('let $FOO /= 1', 'E734') + call assert_fails('let $FOO %= 1', 'E734') + let $FOO .= 's' + call assert_equal('1s', $FOO) + unlet $FOO + + " Test for option variable (type: number) + let &scrolljump = 1 + let &scrolljump += 5 + call assert_equal(6, &scrolljump) + let &scrolljump -= 2 + call assert_equal(4, &scrolljump) + let &scrolljump *= 3 + call assert_equal(12, &scrolljump) + let &scrolljump /= 2 + call assert_equal(6, &scrolljump) + let &scrolljump %= 5 + call assert_equal(1, &scrolljump) + call assert_fails('let &scrolljump .= "j"', 'E734') + set scrolljump&vim + + " Test for register + let @/ = 1 + call assert_fails('let @/ += 1', 'E734') + call assert_fails('let @/ -= 1', 'E734') + call assert_fails('let @/ *= 1', 'E734') + call assert_fails('let @/ /= 1', 'E734') + call assert_fails('let @/ %= 1', 'E734') + let @/ .= 's' + call assert_equal('1s', @/) + let @/ = '' + endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker *** ../vim-8.1.0901/src/version.c 2019-02-12 22:15:03.073282144 +0100 --- src/version.c 2019-02-12 22:22:34.399217184 +0100 *************** *** 785,786 **** --- 785,788 ---- { /* Add new patch number below this line */ + /**/ + 902, /**/ -- This message contains 78% recycled characters. /// 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 ///