0
0
mirror of https://github.com/vim/vim.git synced 2025-07-26 11:04:33 -04:00

patch 8.1.0902: incomplete set of assignment operators

Problem:    Incomplete set of assignment operators.
Solution:   Add /=, *= and %=. (Ozaki Kiichi, closes #3931)
This commit is contained in:
Bram Moolenaar 2019-02-12 22:28:33 +01:00
parent 57ee2b6e0b
commit ff697e6cef
4 changed files with 140 additions and 34 deletions

View File

@ -10836,9 +10836,13 @@ This does NOT work: >
When the selected range of items is partly past the When the selected range of items is partly past the
end of the list, items will be added. 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}".
: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 These fail if {var} was not set yet and when the type
of {var} and {expr1} don't fit the operator. of {var} and {expr1} don't fit the operator.

View File

@ -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 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. * ":let [var1, var2] = expr" unpack list.
*/ */
@ -1216,10 +1219,10 @@ ex_let(exarg_T *eap)
argend = skip_var_list(arg, &var_count, &semicolon); argend = skip_var_list(arg, &var_count, &semicolon);
if (argend == NULL) if (argend == NULL)
return; return;
if (argend > arg && argend[-1] == '.') /* for var.='str' */ if (argend > arg && argend[-1] == '.') // for var.='str'
--argend; --argend;
expr = skipwhite(argend); expr = skipwhite(argend);
if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL if (*expr != '=' && !(vim_strchr((char_u *)"+-*/%.", *expr) != NULL
&& expr[1] == '=')) && expr[1] == '='))
{ {
/* /*
@ -1249,8 +1252,8 @@ ex_let(exarg_T *eap)
op[1] = NUL; op[1] = NUL;
if (*expr != '=') if (*expr != '=')
{ {
if (vim_strchr((char_u *)"+-.", *expr) != NULL) if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL)
op[0] = *expr; /* +=, -= or .= */ op[0] = *expr; // +=, -=, *=, /=, %= or .=
expr = skipwhite(expr + 2); expr = skipwhite(expr + 2);
} }
else else
@ -1671,7 +1674,7 @@ ex_let_one(
semsg(_(e_invarg2), name - 1); semsg(_(e_invarg2), name - 1);
else else
{ {
if (op != NULL && (*op == '+' || *op == '-')) if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL)
semsg(_(e_letwrong), op); semsg(_(e_letwrong), op);
else if (endchars != NULL else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg)) == NULL) && vim_strchr(endchars, *skipwhite(arg)) == NULL)
@ -1744,18 +1747,22 @@ ex_let_one(
|| (opt_type == 0 && *op != '.')) || (opt_type == 0 && *op != '.'))
{ {
semsg(_(e_letwrong), op); semsg(_(e_letwrong), op);
s = NULL; /* don't set the value */ s = NULL; // don't set the value
} }
else else
{ {
if (opt_type == 1) /* number */ if (opt_type == 1) // number
{ {
if (*op == '+') switch (*op)
n = numval + n; {
else case '+': n = numval + n; break;
n = numval - n; 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); s = concat_str(stringval, s);
vim_free(stringval); vim_free(stringval);
@ -1779,7 +1786,7 @@ ex_let_one(
else if (*arg == '@') else if (*arg == '@')
{ {
++arg; ++arg;
if (op != NULL && (*op == '+' || *op == '-')) if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL)
semsg(_(e_letwrong), op); semsg(_(e_letwrong), op);
else if (endchars != NULL else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg + 1)) == 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". * Set a variable that was parsed by get_lval() to "rettv".
* "endp" points to just after the parsed name. * "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 static void
set_var_lval( set_var_lval(
@ -2327,7 +2335,7 @@ set_var_lval(
{ {
typval_T tv; typval_T tv;
/* handle +=, -= and .= */ // handle +=, -=, *=, /=, %= and .=
di = NULL; di = NULL;
if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name), if (get_var_tv(lp->ll_name, (int)STRLEN(lp->ll_name),
&tv, &di, TRUE, FALSE) == OK) &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. * Returns OK or FAIL.
*/ */
static int static int
@ -2490,7 +2499,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
case VAR_LIST: case VAR_LIST:
if (*op != '+' || tv2->v_type != VAR_LIST) if (*op != '+' || tv2->v_type != VAR_LIST)
break; break;
/* List += List */ // List += List
if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL) if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL)
list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL); list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
return OK; return OK;
@ -2499,19 +2508,24 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
case VAR_STRING: case VAR_STRING:
if (tv2->v_type == VAR_LIST) if (tv2->v_type == VAR_LIST)
break; break;
if (*op == '+' || *op == '-') if (vim_strchr((char_u *)"+-*/%", *op) != NULL)
{ {
/* nr += nr or nr -= nr*/ // nr += nr , nr -= nr , nr *=nr , nr /= nr , nr %= nr
n = tv_get_number(tv1); n = tv_get_number(tv1);
#ifdef FEAT_FLOAT #ifdef FEAT_FLOAT
if (tv2->v_type == VAR_FLOAT) if (tv2->v_type == VAR_FLOAT)
{ {
float_T f = n; float_T f = n;
if (*op == '+') if (*op == '%')
f += tv2->vval.v_float; break;
else switch (*op)
f -= tv2->vval.v_float; {
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); clear_tv(tv1);
tv1->v_type = VAR_FLOAT; tv1->v_type = VAR_FLOAT;
tv1->vval.v_float = f; tv1->vval.v_float = f;
@ -2519,10 +2533,14 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
else else
#endif #endif
{ {
if (*op == '+') switch (*op)
n += tv_get_number(tv2); {
else case '+': n += tv_get_number(tv2); break;
n -= tv_get_number(tv2); 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); clear_tv(tv1);
tv1->v_type = VAR_NUMBER; tv1->v_type = VAR_NUMBER;
tv1->vval.v_number = n; tv1->vval.v_number = n;
@ -2533,7 +2551,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
if (tv2->v_type == VAR_FLOAT) if (tv2->v_type == VAR_FLOAT)
break; break;
/* str .= str */ // str .= str
s = tv_get_string(tv1); s = tv_get_string(tv1);
s = concat_str(s, tv_get_string_buf(tv2, numbuf)); s = concat_str(s, tv_get_string_buf(tv2, numbuf));
clear_tv(tv1); clear_tv(tv1);
@ -2547,7 +2565,8 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
{ {
float_T f; 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_NUMBER
&& tv2->v_type != VAR_STRING)) && tv2->v_type != VAR_STRING))
break; break;
@ -2555,10 +2574,13 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
f = tv2->vval.v_float; f = tv2->vval.v_float;
else else
f = tv_get_number(tv2); f = tv_get_number(tv2);
if (*op == '+') switch (*op)
tv1->vval.v_float += f; {
else case '+': tv1->vval.v_float += f; break;
tv1->vval.v_float -= f; case '-': tv1->vval.v_float -= f; break;
case '*': tv1->vval.v_float *= f; break;
case '/': tv1->vval.v_float /= f; break;
}
} }
#endif #endif
return OK; return OK;

View File

@ -1441,6 +1441,84 @@ func Test_script_local_func()
enew! | close enew! | close
endfunc 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 " Modelines {{{1
" vim: ts=8 sw=4 tw=80 fdm=marker " vim: ts=8 sw=4 tw=80 fdm=marker

View File

@ -783,6 +783,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 */
/**/
902,
/**/ /**/
901, 901,
/**/ /**/