changeset 15790:05d836c8f1c4 v8.1.0902

patch 8.1.0902: incomplete set of assignment operators commit https://github.com/vim/vim/commit/ff697e6cef8ced7717a21fd525ab3200b2f1724f Author: Bram Moolenaar <Bram@vim.org> Date: Tue Feb 12 22:28:33 2019 +0100 patch 8.1.0902: incomplete set of assignment operators Problem: Incomplete set of assignment operators. Solution: Add /=, *= and %=. (Ozaki Kiichi, closes https://github.com/vim/vim/issues/3931)
author Bram Moolenaar <Bram@vim.org>
date Tue, 12 Feb 2019 22:30:08 +0100
parents 9b7a86acea77
children 25bb0ae51c55
files runtime/doc/eval.txt src/eval.c src/testdir/test_vimscript.vim src/version.c
diffstat 4 files changed, 141 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -10836,9 +10836,13 @@ 7. Commands						*expression-commands*
 			When the selected range of items is partly past the
 			end of the list, items will be added.
 
-					*:let+=* *:let-=* *:let.=* *E734*
+                                            *: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.
--- a/src/eval.c
+++ b/src/eval.c
@@ -1197,6 +1197,9 @@ eval_foldexpr(char_u *arg, int *cp)
  * ":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,10 +1219,10 @@ ex_let(exarg_T *eap)
     argend = skip_var_list(arg, &var_count, &semicolon);
     if (argend == NULL)
 	return;
-    if (argend > arg && argend[-1] == '.')  /* for var.='str' */
+    if (argend > arg && argend[-1] == '.')  // for var.='str'
 	--argend;
     expr = skipwhite(argend);
-    if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL
+    if (*expr != '=' && !(vim_strchr((char_u *)"+-*/%.", *expr) != NULL
 			  && expr[1] == '='))
     {
 	/*
@@ -1249,8 +1252,8 @@ ex_let(exarg_T *eap)
 	op[1] = NUL;
 	if (*expr != '=')
 	{
-	    if (vim_strchr((char_u *)"+-.", *expr) != NULL)
-		op[0] = *expr;   /* +=, -= or .= */
+	    if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
+		op[0] = *expr;   // +=, -=, *=, /=, %= or .=
 	    expr = skipwhite(expr + 2);
 	}
 	else
@@ -1671,7 +1674,7 @@ ex_let_one(
 	    semsg(_(e_invarg2), name - 1);
 	else
 	{
-	    if (op != NULL && (*op == '+' || *op == '-'))
+	    if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL)
 		semsg(_(e_letwrong), op);
 	    else if (endchars != NULL
 			     && vim_strchr(endchars, *skipwhite(arg)) == NULL)
@@ -1744,18 +1747,22 @@ ex_let_one(
 			|| (opt_type == 0 && *op != '.'))
 		{
 		    semsg(_(e_letwrong), op);
-		    s = NULL;  /* don't set the value */
+		    s = NULL;  // don't set the value
 		}
 		else
 		{
-		    if (opt_type == 1)  /* number */
+		    if (opt_type == 1)  // number
 		    {
-			if (*op == '+')
-			    n = numval + n;
-			else
-			    n = numval - n;
+			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 */
+		    else if (opt_type == 0 && stringval != NULL) // string
 		    {
 			s = concat_str(stringval, s);
 			vim_free(stringval);
@@ -1779,7 +1786,7 @@ ex_let_one(
     else if (*arg == '@')
     {
 	++arg;
-	if (op != NULL && (*op == '+' || *op == '-'))
+	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,7 +2261,8 @@ clear_lval(lval_T *lp)
 /*
  * 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 "=".
+ * "op" is NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=",
+ * "%" for "%=", "." for ".=" or "=" for "=".
  */
     static void
 set_var_lval(
@@ -2327,7 +2335,7 @@ set_var_lval(
 	{
 	    typval_T tv;
 
-	    /* handle +=, -= and .= */
+	    // handle +=, -=, *=, /=, %= and .=
 	    di = NULL;
 	    if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
 					     &tv, &di, TRUE, FALSE) == OK)
@@ -2448,7 +2456,8 @@ set_var_lval(
 }
 
 /*
- * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2"
+ * Handle "tv1 += tv2", "tv1 -= tv2", "tv1 *= tv2", "tv1 /= tv2", "tv1 %= tv2"
+ * and "tv1 .= tv2"
  * Returns OK or FAIL.
  */
     static int
@@ -2490,7 +2499,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 	    case VAR_LIST:
 		if (*op != '+' || tv2->v_type != VAR_LIST)
 		    break;
-		/* List += List */
+		// 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,19 +2508,24 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 	    case VAR_STRING:
 		if (tv2->v_type == VAR_LIST)
 		    break;
-		if (*op == '+' || *op == '-')
-		{
-		    /* nr += nr  or  nr -= nr*/
+		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 == '+')
-			    f += tv2->vval.v_float;
-			else
-			    f -= tv2->vval.v_float;
+			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,10 +2533,14 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 		    else
 #endif
 		    {
-			if (*op == '+')
-			    n += tv_get_number(tv2);
-			else
-			    n -= tv_get_number(tv2);
+			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,7 +2551,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 		    if (tv2->v_type == VAR_FLOAT)
 			break;
 
-		    /* str .= str */
+		    // str .= str
 		    s = tv_get_string(tv1);
 		    s = concat_str(s, tv_get_string_buf(tv2, numbuf));
 		    clear_tv(tv1);
@@ -2547,7 +2565,8 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 		{
 		    float_T f;
 
-		    if (*op == '.' || (tv2->v_type != VAR_FLOAT
+		    if (*op == '%' || *op == '.'
+				   || (tv2->v_type != VAR_FLOAT
 				    && tv2->v_type != VAR_NUMBER
 				    && tv2->v_type != VAR_STRING))
 			break;
@@ -2555,10 +2574,13 @@ tv_op(typval_T *tv1, typval_T *tv2, char
 			f = tv2->vval.v_float;
 		    else
 			f = tv_get_number(tv2);
-		    if (*op == '+')
-			tv1->vval.v_float += f;
-		    else
-			tv1->vval.v_float -= f;
+		    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;
--- a/src/testdir/test_vimscript.vim
+++ b/src/testdir/test_vimscript.vim
@@ -1441,6 +1441,84 @@ func Test_script_local_func()
   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
--- a/src/version.c
+++ b/src/version.c
@@ -784,6 +784,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    902,
+/**/
     901,
 /**/
     900,