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:
parent
57ee2b6e0b
commit
ff697e6cef
@ -10836,9 +10836,13 @@ This does NOT work: >
|
||||
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.
|
||||
|
88
src/eval.c
88
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_u *op)
|
||||
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_u *op)
|
||||
case VAR_STRING:
|
||||
if (tv2->v_type == VAR_LIST)
|
||||
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);
|
||||
#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_u *op)
|
||||
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_u *op)
|
||||
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_u *op)
|
||||
{
|
||||
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_u *op)
|
||||
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;
|
||||
|
@ -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
|
||||
|
@ -783,6 +783,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
902,
|
||||
/**/
|
||||
901,
|
||||
/**/
|
||||
|
Loading…
x
Reference in New Issue
Block a user