aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin M. Keyes <justinkz@gmail.com>2019-06-02 23:32:28 +0200
committerJustin M. Keyes <justinkz@gmail.com>2019-06-02 23:32:28 +0200
commit8a33cb32bac1632a98d13df7865faacadfc41391 (patch)
tree2e5c51d46faa0e6a0a99f312ef506edb21b5c69a
parentb65a7b7f6692da9c9b18a1fb68817644a119fbed (diff)
parentbfc44a91acdfb968cb3d0d21effc4ea9cbcd09e1 (diff)
downloadrneovim-8a33cb32bac1632a98d13df7865faacadfc41391.tar.gz
rneovim-8a33cb32bac1632a98d13df7865faacadfc41391.tar.bz2
rneovim-8a33cb32bac1632a98d13df7865faacadfc41391.zip
Merge #10086 'vim-patch:8.1.{902,1114}'
-rw-r--r--runtime/doc/eval.txt78
-rw-r--r--src/nvim/eval.c38
-rw-r--r--src/nvim/eval/executor.c44
-rw-r--r--src/nvim/testdir/test_eval_stuff.vim29
-rw-r--r--src/nvim/testdir/test_vimscript.vim78
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