vim-patch:8.1.0902: incomplete set of assignment operators

Problem:    Incomplete set of assignment operators.
Solution:   Add /=, *= and %=. (Ozaki Kiichi, closes vim/vim#3931)
ff697e6cef
This commit is contained in:
erw7 2019-05-29 09:33:43 +09:00
parent f8f63393c1
commit d46aaa0746
4 changed files with 126 additions and 30 deletions

View File

@ -9459,9 +9459,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

@ -1442,6 +1442,9 @@ int 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.
*/ */
@ -1465,7 +1468,7 @@ void ex_let(exarg_T *eap)
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] == '=')) {
// ":let" without "=": list variables // ":let" without "=": list variables
if (*arg == '[') { if (*arg == '[') {
@ -1488,8 +1491,8 @@ void ex_let(exarg_T *eap)
op[0] = '='; op[0] = '=';
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; // +=, -=, .= op[0] = *expr; // +=, -=, *=, /=, %= or .=
} }
expr = skipwhite(expr + 2); expr = skipwhite(expr + 2);
} else { } else {
@ -1864,7 +1867,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
if (len == 0) { if (len == 0) {
EMSG2(_(e_invarg2), name - 1); EMSG2(_(e_invarg2), name - 1);
} else { } else {
if (op != NULL && (*op == '+' || *op == '-')) { if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) {
EMSG2(_(e_letwrong), op); EMSG2(_(e_letwrong), op);
} else if (endchars != NULL } else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg)) == NULL) { && vim_strchr(endchars, *skipwhite(arg)) == NULL) {
@ -1927,10 +1930,12 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
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; case '+': n = numval + n; break;
} else { case '-': n = numval - n; break;
n = numval - n; 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
char *const oldstringval = stringval; char *const oldstringval = stringval;
@ -1951,7 +1956,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv,
// ":let @r = expr": Set register contents. // ":let @r = expr": Set register contents.
} else if (*arg == '@') { } else if (*arg == '@') {
arg++; arg++;
if (op != NULL && (*op == '+' || *op == '-')) { if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) {
emsgf(_(e_letwrong), op); emsgf(_(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) {
@ -2350,7 +2355,8 @@ static void 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 set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
int copy, const char_u *op) int copy, const char_u *op)
@ -2365,7 +2371,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv,
if (op != NULL && *op != '=') { if (op != NULL && *op != '=') {
typval_T tv; typval_T tv;
// handle +=, -= and .= // handle +=, -=, *=, /=, %= and .=
di = NULL; di = NULL;
if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name), if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name),
&tv, &di, true, false) == OK) { &tv, &di, true, false) == OK) {

View File

@ -16,7 +16,7 @@ static char *e_letwrong = N_("E734: Wrong variable type for %s=");
char *e_listidx = N_("E684: list index out of range: %" PRId64); char *e_listidx = N_("E684: list index out of range: %" PRId64);
/// Hanle tv1 += tv2, -=, .= /// Hanle tv1 += tv2, -=, *=, /=, %=, .=
/// ///
/// @param[in,out] tv1 First operand, modified typval. /// @param[in,out] tv1 First operand, modified typval.
/// @param[in] tv2 Second operand. /// @param[in] tv2 Second operand.
@ -51,25 +51,31 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
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 or nr -= nr, nr *= nr, nr /= nr, nr %= nr
varnumber_T n = tv_get_number(tv1); varnumber_T n = tv_get_number(tv1);
if (tv2->v_type == VAR_FLOAT) { if (tv2->v_type == VAR_FLOAT) {
float_T f = (float_T)n; float_T f = (float_T)n;
if (*op == '+') { if (*op == '%') {
f += tv2->vval.v_float; break;
} else { }
f -= tv2->vval.v_float; 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;
} }
tv_clear(tv1); tv_clear(tv1);
tv1->v_type = VAR_FLOAT; tv1->v_type = VAR_FLOAT;
tv1->vval.v_float = f; tv1->vval.v_float = f;
} else { } else {
if (*op == '+') { switch (*op) {
n += tv_get_number(tv2); case '+': n += tv_get_number(tv2); break;
} 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;
} }
tv_clear(tv1); tv_clear(tv1);
tv1->v_type = VAR_NUMBER; tv1->v_type = VAR_NUMBER;
@ -92,18 +98,20 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
return OK; return OK;
} }
case VAR_FLOAT: { case VAR_FLOAT: {
if (*op == '.' || (tv2->v_type != VAR_FLOAT if (*op == '%' || *op == '.'
&& tv2->v_type != VAR_NUMBER || (tv2->v_type != VAR_FLOAT
&& tv2->v_type != VAR_STRING)) { && tv2->v_type != VAR_NUMBER
&& tv2->v_type != VAR_STRING)) {
break; break;
} }
const float_T f = (tv2->v_type == VAR_FLOAT const float_T f = (tv2->v_type == VAR_FLOAT
? tv2->vval.v_float ? tv2->vval.v_float
: (float_T)tv_get_number(tv2)); : (float_T)tv_get_number(tv2));
if (*op == '+') { switch (*op) {
tv1->vval.v_float += f; case '+': tv1->vval.v_float += f; break;
} 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;
} }
return OK; return OK;
} }

View File

@ -1294,6 +1294,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