diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2019-06-02 23:32:28 +0200 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2019-06-02 23:32:28 +0200 |
commit | 8a33cb32bac1632a98d13df7865faacadfc41391 (patch) | |
tree | 2e5c51d46faa0e6a0a99f312ef506edb21b5c69a | |
parent | b65a7b7f6692da9c9b18a1fb68817644a119fbed (diff) | |
parent | bfc44a91acdfb968cb3d0d21effc4ea9cbcd09e1 (diff) | |
download | rneovim-8a33cb32bac1632a98d13df7865faacadfc41391.tar.gz rneovim-8a33cb32bac1632a98d13df7865faacadfc41391.tar.bz2 rneovim-8a33cb32bac1632a98d13df7865faacadfc41391.zip |
Merge #10086 'vim-patch:8.1.{902,1114}'
-rw-r--r-- | runtime/doc/eval.txt | 78 | ||||
-rw-r--r-- | src/nvim/eval.c | 38 | ||||
-rw-r--r-- | src/nvim/eval/executor.c | 44 | ||||
-rw-r--r-- | src/nvim/testdir/test_eval_stuff.vim | 29 | ||||
-rw-r--r-- | src/nvim/testdir/test_vimscript.vim | 78 |
5 files changed, 208 insertions, 59 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index 17ed0b2d0f..00194b4613 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -610,10 +610,10 @@ Expression syntax summary, from least to most significant: expr2 ? expr1 : expr1 if-then-else |expr2| expr3 - expr3 || expr3 .. logical OR + expr3 || expr3 ... logical OR |expr3| expr4 - expr4 && expr4 .. logical AND + expr4 && expr4 ... logical AND |expr4| expr5 expr5 == expr5 equal @@ -634,14 +634,15 @@ Expression syntax summary, from least to most significant: expr5 isnot expr5 different |List| instance |expr5| expr6 - expr6 + expr6 .. number addition or list concatenation - expr6 - expr6 .. number subtraction - expr6 . expr6 .. string concatenation + expr6 + expr6 ... number addition, list or blob concatenation + expr6 - expr6 ... number subtraction + expr6 . expr6 ... string concatenation + expr6 .. expr6 ... string concatenation |expr6| expr7 - expr7 * expr7 .. number multiplication - expr7 / expr7 .. number division - expr7 % expr7 .. number modulo + expr7 * expr7 ... number multiplication + expr7 / expr7 ... number division + expr7 % expr7 ... number modulo |expr7| expr8 ! expr7 logical NOT @@ -670,7 +671,7 @@ Expression syntax summary, from least to most significant: {args -> expr1} lambda expression -".." indicates that the operations in this level can be concatenated. +"..." indicates that the operations in this level can be concatenated. Example: > &nu || &list && &shell == "csh" @@ -707,7 +708,9 @@ use in a variable such as "a:1". expr2 and expr3 *expr2* *expr3* --------------- - *expr-barbar* *expr-&&* +expr3 || expr3 .. logical OR *expr-barbar* +expr4 && expr4 .. logical AND *expr-&&* + The "||" and "&&" operators take one argument on each side. The arguments are (converted to) Numbers. The result is: @@ -847,18 +850,22 @@ can be matched like an ordinary character. Examples: expr5 and expr6 *expr5* *expr6* --------------- -expr6 + expr6 .. Number addition or |List| concatenation *expr-+* -expr6 - expr6 .. Number subtraction *expr--* -expr6 . expr6 .. String concatenation *expr-.* +expr6 + expr6 Number addition, |List| or |Blob| concatenation *expr-+* +expr6 - expr6 Number subtraction *expr--* +expr6 . expr6 String concatenation *expr-.* +expr6 .. expr6 String concatenation *expr-..* For |Lists| only "+" is possible and then both expr6 must be a list. The result is a new list with the two lists Concatenated. -expr7 * expr7 .. Number multiplication *expr-star* -expr7 / expr7 .. Number division *expr-/* -expr7 % expr7 .. Number modulo *expr-%* +For String concatenation ".." is preferred, since "." is ambiguous, it is also +used for |Dict| member access and floating point numbers. + +expr7 * expr7 Number multiplication *expr-star* +expr7 / expr7 Number division *expr-/* +expr7 % expr7 Number modulo *expr-%* -For all, except ".", Strings are converted to Numbers. +For all, except "." and "..", Strings are converted to Numbers. For bitwise operators see |and()|, |or()| and |xor()|. Note the difference between "+" and ".": @@ -1049,11 +1056,6 @@ These are INVALID: 3. empty {M} 1e40 missing .{M} - *float-pi* *float-e* -A few useful values to copy&paste: > - :let pi = 3.14159265359 - :let e = 2.71828182846 - Rationale: Before floating point was introduced, the text "123.456" was interpreted as the two numbers "123" and "456", both converted to a string and concatenated, @@ -1062,6 +1064,15 @@ could not find it intentionally being used in Vim scripts, this backwards incompatibility was accepted in favor of being able to use the normal notation for floating point numbers. + *float-pi* *float-e* +A few useful values to copy&paste: > + :let pi = 3.14159265359 + :let e = 2.71828182846 +Or, if you don't want to write them in as floating-point literals, you can +also use functions, like the following: > + :let pi = acos(-1.0) + :let e = exp(1.0) +< *floating-point-precision* The precision and range of floating points numbers depends on what "double" means in the library Vim was compiled with. There is no way to change this at @@ -1101,8 +1112,10 @@ A string constant accepts these special characters: \\ backslash \" double quote \<xxx> Special key named "xxx". e.g. "\<C-W>" for CTRL-W. This is for use - in mappings, the 0x80 byte is escaped. Don't use <Char-xxxx> to get a - utf-8 character, use \uxxxx as mentioned above. + in mappings, the 0x80 byte is escaped. + To use the double quote character it must be escaped: "<M-\">". + Don't use <Char-xxxx> to get a utf-8 character, use \uxxxx as + mentioned above. Note that "\xff" is stored as the byte 255, which may be invalid in some encodings. Use "\u00ff" to store character 255 correctly as UTF-8. @@ -1211,8 +1224,8 @@ The arguments are optional. Example: > *closure* Lambda expressions can access outer scope variables and arguments. This is often called a closure. Example where "i" and "a:arg" are used in a lambda -while they exist in the function scope. They remain valid even after the -function returns: > +while they already exist in the function scope. They remain valid even after +the function returns: > :function Foo(arg) : let i = 3 : return {x -> x + i - a:arg} @@ -1220,8 +1233,11 @@ function returns: > :let Bar = Foo(4) :echo Bar(6) < 5 -See also |:func-closure|. Lambda and closure support can be checked with: > - if has('lambda') +Note that the variables must exist in the outer scope before the lamba is +defined for this to work. See also |:func-closure|. + +Lambda and closure support can be checked with: > + if has('lambda') Examples for using a lambda expression with |sort()|, |map()| and |filter()|: > :echo map([1, 2, 3], {idx, val -> val + 1}) @@ -9459,9 +9475,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. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index deaed17926..1640729c94 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -1442,7 +1442,11 @@ 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 [var1, var2] = expr" unpack list. */ void ex_let(exarg_T *eap) @@ -1465,8 +1469,8 @@ void ex_let(exarg_T *eap) argend--; } expr = skipwhite(argend); - if (*expr != '=' && !(vim_strchr((char_u *)"+-.", *expr) != NULL - && expr[1] == '=')) { + if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%.", *expr) != NULL + && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0)) { // ":let" without "=": list variables if (*arg == '[') { EMSG(_(e_invarg)); @@ -1488,8 +1492,11 @@ void ex_let(exarg_T *eap) op[0] = '='; op[1] = NUL; if (*expr != '=') { - if (vim_strchr((char_u *)"+-.", *expr) != NULL) { - op[0] = *expr; // +=, -=, .= + if (vim_strchr((char_u *)"+-*/%.", *expr) != NULL) { + op[0] = *expr; // +=, -=, *=, /=, %= or .= + if (expr[0] == '.' && expr[1] == '.') { // ..= + expr++; + } } expr = skipwhite(expr + 2); } else { @@ -1864,7 +1871,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, if (len == 0) { EMSG2(_(e_invarg2), name - 1); } else { - if (op != NULL && (*op == '+' || *op == '-')) { + if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) { EMSG2(_(e_letwrong), op); } else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg)) == NULL) { @@ -1927,10 +1934,12 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, s = NULL; // don't set the value } else { 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 char *const oldstringval = stringval; @@ -1951,7 +1960,7 @@ static char_u *ex_let_one(char_u *arg, typval_T *const tv, // ":let @r = expr": Set register contents. } else if (*arg == '@') { arg++; - if (op != NULL && (*op == '+' || *op == '-')) { + if (op != NULL && vim_strchr((char_u *)"+-*/%", *op) != NULL) { emsgf(_(e_letwrong), op); } else if (endchars != NULL && vim_strchr(endchars, *skipwhite(arg + 1)) == NULL) { @@ -2350,7 +2359,8 @@ static void 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(lval_T *lp, char_u *endp, typval_T *rettv, int copy, const char_u *op) @@ -2365,7 +2375,7 @@ static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, if (op != NULL && *op != '=') { typval_T tv; - // handle +=, -= and .= + // handle +=, -=, *=, /=, %= and .= di = NULL; if (get_var_tv((const char *)lp->ll_name, (int)STRLEN(lp->ll_name), &tv, &di, true, false) == OK) { @@ -3783,6 +3793,7 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) * + number addition * - number subtraction * . string concatenation + * .. string concatenation * * "arg" must point to the first non-white of the expression. * "arg" is advanced to the next non-white after the recognized expression. @@ -3830,6 +3841,9 @@ static int eval5(char_u **arg, typval_T *rettv, int evaluate) /* * Get the second variable. */ + if (op == '.' && *(*arg + 1) == '.') { // ..string concatenation + (*arg)++; + } *arg = skipwhite(*arg + 1); if (eval6(arg, &var2, evaluate, op == '.') == FAIL) { tv_clear(rettv); diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c index 99298cbbcf..e972c506dd 100644 --- a/src/nvim/eval/executor.c +++ b/src/nvim/eval/executor.c @@ -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); -/// Hanle tv1 += tv2, -=, .= +/// Hanle tv1 += tv2, -=, *=, /=, %=, .= /// /// @param[in,out] tv1 First operand, modified typval. /// @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) { break; } - if (*op == '+' || *op == '-') { - // nr += nr or nr -= nr + if (vim_strchr((char_u *)"+-*/%", *op) != NULL) { + // nr += nr or nr -= nr, nr *= nr, nr /= nr, nr %= nr varnumber_T n = tv_get_number(tv1); if (tv2->v_type == VAR_FLOAT) { float_T f = (float_T)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; } tv_clear(tv1); tv1->v_type = VAR_FLOAT; tv1->vval.v_float = f; } else { - 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; } tv_clear(tv1); tv1->v_type = VAR_NUMBER; @@ -92,18 +98,20 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2, return OK; } case VAR_FLOAT: { - if (*op == '.' || (tv2->v_type != VAR_FLOAT - && tv2->v_type != VAR_NUMBER - && tv2->v_type != VAR_STRING)) { + if (*op == '%' || *op == '.' + || (tv2->v_type != VAR_FLOAT + && tv2->v_type != VAR_NUMBER + && tv2->v_type != VAR_STRING)) { break; } const float_T f = (tv2->v_type == VAR_FLOAT ? tv2->vval.v_float : (float_T)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; } return OK; } diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 1850fb0cf1..19a15590e5 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -49,3 +49,32 @@ func Test_line_continuation() "\ and some more call assert_equal([5, 6], array) endfunc + +func Test_string_concatenation() + call assert_equal('ab', 'a'.'b') + call assert_equal('ab', 'a' .'b') + call assert_equal('ab', 'a'. 'b') + call assert_equal('ab', 'a' . 'b') + + call assert_equal('ab', 'a'..'b') + call assert_equal('ab', 'a' ..'b') + call assert_equal('ab', 'a'.. 'b') + call assert_equal('ab', 'a' .. 'b') + + let a = 'a' + let b = 'b' + let a .= b + call assert_equal('ab', a) + + let a = 'a' + let a.=b + call assert_equal('ab', a) + + let a = 'a' + let a ..= b + call assert_equal('ab', a) + + let a = 'a' + let a..=b + call assert_equal('ab', a) +endfunc diff --git a/src/nvim/testdir/test_vimscript.vim b/src/nvim/testdir/test_vimscript.vim index 5b16f6d205..05abf04d65 100644 --- a/src/nvim/testdir/test_vimscript.vim +++ b/src/nvim/testdir/test_vimscript.vim @@ -1294,6 +1294,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 |