mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #23084 from zeertzjq/vim-8.2.1794
vim-patch:8.2.{1794,1798},9.0.1452
This commit is contained in:
commit
c15939c1f7
@ -93,7 +93,27 @@ non-zero number it means TRUE: >
|
|||||||
:" executed
|
:" executed
|
||||||
To test for a non-empty string, use empty(): >
|
To test for a non-empty string, use empty(): >
|
||||||
:if !empty("foo")
|
:if !empty("foo")
|
||||||
<
|
|
||||||
|
< *falsy* *truthy*
|
||||||
|
An expression can be used as a condition, ignoring the type and only using
|
||||||
|
whether the value is "sort of true" or "sort of false". Falsy is:
|
||||||
|
the number zero
|
||||||
|
empty string, blob, list or dictionary
|
||||||
|
Other values are truthy. Examples:
|
||||||
|
0 falsy
|
||||||
|
1 truthy
|
||||||
|
-1 truthy
|
||||||
|
0.0 falsy
|
||||||
|
0.1 truthy
|
||||||
|
'' falsy
|
||||||
|
'x' truthy
|
||||||
|
[] falsy
|
||||||
|
[0] truthy
|
||||||
|
{} falsy
|
||||||
|
#{x: 1} truthy
|
||||||
|
0z falsy
|
||||||
|
0z00 truthy
|
||||||
|
|
||||||
*non-zero-arg*
|
*non-zero-arg*
|
||||||
Function arguments often behave slightly different from |TRUE|: If the
|
Function arguments often behave slightly different from |TRUE|: If the
|
||||||
argument is present and it evaluates to a non-zero Number, |v:true| or a
|
argument is present and it evaluates to a non-zero Number, |v:true| or a
|
||||||
@ -841,9 +861,12 @@ All expressions within one level are parsed from left to right.
|
|||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
expr1 *expr1* *ternary* *E109*
|
expr1 *expr1* *ternary* *falsy-operator* *??* *E109*
|
||||||
|
|
||||||
expr2 ? expr1 : expr1
|
The ternary operator: expr2 ? expr1 : expr1
|
||||||
|
The falsy operator: expr2 ?? expr1
|
||||||
|
|
||||||
|
Ternary operator ~
|
||||||
|
|
||||||
The expression before the '?' is evaluated to a number. If it evaluates to
|
The expression before the '?' is evaluated to a number. If it evaluates to
|
||||||
|TRUE|, the result is the value of the expression between the '?' and ':',
|
|TRUE|, the result is the value of the expression between the '?' and ':',
|
||||||
@ -866,6 +889,23 @@ To keep this readable, using |line-continuation| is suggested: >
|
|||||||
You should always put a space before the ':', otherwise it can be mistaken for
|
You should always put a space before the ':', otherwise it can be mistaken for
|
||||||
use in a variable such as "a:1".
|
use in a variable such as "a:1".
|
||||||
|
|
||||||
|
Falsy operator ~
|
||||||
|
|
||||||
|
This is also known as the "null coalescing operator", but that's too
|
||||||
|
complicated, thus we just call it the falsy operator.
|
||||||
|
|
||||||
|
The expression before the '??' is evaluated. If it evaluates to
|
||||||
|
|truthy|, this is used as the result. Otherwise the expression after the '??'
|
||||||
|
is evaluated and used as the result. This is most useful to have a default
|
||||||
|
value for an expression that may result in zero or empty: >
|
||||||
|
echo theList ?? 'list is empty'
|
||||||
|
echo GetName() ?? 'unknown'
|
||||||
|
|
||||||
|
These are similar, but not equal: >
|
||||||
|
expr2 ?? expr1
|
||||||
|
expr2 ? expr2 : expr1
|
||||||
|
In the second line "expr2" is evaluated twice.
|
||||||
|
|
||||||
|
|
||||||
------------------------------------------------------------------------------
|
------------------------------------------------------------------------------
|
||||||
expr2 and expr3 *expr2* *expr3*
|
expr2 and expr3 *expr2* *expr3*
|
||||||
|
@ -2336,6 +2336,7 @@ int eval0(char *arg, typval_T *rettv, exarg_T *eap, evalarg_T *const evalarg)
|
|||||||
|
|
||||||
/// Handle top level expression:
|
/// Handle top level expression:
|
||||||
/// expr2 ? expr1 : expr1
|
/// expr2 ? expr1 : expr1
|
||||||
|
/// expr2 ?? expr1
|
||||||
///
|
///
|
||||||
/// "arg" must point to the first non-white of the expression.
|
/// "arg" must point to the first non-white of the expression.
|
||||||
/// "arg" is advanced to the next non-white after the recognized expression.
|
/// "arg" is advanced to the next non-white after the recognized expression.
|
||||||
@ -2352,6 +2353,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
|||||||
|
|
||||||
char *p = *arg;
|
char *p = *arg;
|
||||||
if (*p == '?') {
|
if (*p == '?') {
|
||||||
|
const bool op_falsy = p[1] == '?';
|
||||||
evalarg_T *evalarg_used = evalarg;
|
evalarg_T *evalarg_used = evalarg;
|
||||||
evalarg_T local_evalarg;
|
evalarg_T local_evalarg;
|
||||||
if (evalarg == NULL) {
|
if (evalarg == NULL) {
|
||||||
@ -2365,23 +2367,36 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
|||||||
if (evaluate) {
|
if (evaluate) {
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
if (tv_get_number_chk(rettv, &error) != 0) {
|
if (op_falsy) {
|
||||||
|
result = tv2bool(rettv);
|
||||||
|
} else if (tv_get_number_chk(rettv, &error) != 0) {
|
||||||
result = true;
|
result = true;
|
||||||
}
|
}
|
||||||
|
if (error || !op_falsy || !result) {
|
||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
|
}
|
||||||
if (error) {
|
if (error) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the second variable. Recursive!
|
// Get the second variable. Recursive!
|
||||||
|
if (op_falsy) {
|
||||||
|
(*arg)++;
|
||||||
|
}
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
|
evalarg_used->eval_flags = (op_falsy ? !result : result)
|
||||||
if (eval1(arg, rettv, evalarg_used) == FAIL) {
|
? orig_flags : (orig_flags & ~EVAL_EVALUATE);
|
||||||
|
typval_T var2;
|
||||||
|
if (eval1(arg, &var2, evalarg_used) == FAIL) {
|
||||||
evalarg_used->eval_flags = orig_flags;
|
evalarg_used->eval_flags = orig_flags;
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
if (!op_falsy || !result) {
|
||||||
|
*rettv = var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!op_falsy) {
|
||||||
// Check for the ":".
|
// Check for the ":".
|
||||||
p = *arg;
|
p = *arg;
|
||||||
if (*p != ':') {
|
if (*p != ':') {
|
||||||
@ -2395,8 +2410,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
|||||||
|
|
||||||
// Get the third variable. Recursive!
|
// Get the third variable. Recursive!
|
||||||
*arg = skipwhite(*arg + 1);
|
*arg = skipwhite(*arg + 1);
|
||||||
evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
|
evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
|
||||||
typval_T var2;
|
|
||||||
if (eval1(arg, &var2, evalarg_used) == FAIL) {
|
if (eval1(arg, &var2, evalarg_used) == FAIL) {
|
||||||
if (evaluate && result) {
|
if (evaluate && result) {
|
||||||
tv_clear(rettv);
|
tv_clear(rettv);
|
||||||
@ -2407,6 +2421,7 @@ int eval1(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
|||||||
if (evaluate && !result) {
|
if (evaluate && !result) {
|
||||||
*rettv = var2;
|
*rettv = var2;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (evalarg == NULL) {
|
if (evalarg == NULL) {
|
||||||
clear_evalarg(&local_evalarg, NULL);
|
clear_evalarg(&local_evalarg, NULL);
|
||||||
@ -2461,7 +2476,7 @@ static int eval2(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
|||||||
while (p[0] == '|' && p[1] == '|') {
|
while (p[0] == '|' && p[1] == '|') {
|
||||||
// Get the second variable.
|
// Get the second variable.
|
||||||
*arg = skipwhite(*arg + 2);
|
*arg = skipwhite(*arg + 2);
|
||||||
evalarg_used->eval_flags = !result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
|
evalarg_used->eval_flags = !result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
|
||||||
typval_T var2;
|
typval_T var2;
|
||||||
if (eval3(arg, &var2, evalarg_used) == FAIL) {
|
if (eval3(arg, &var2, evalarg_used) == FAIL) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
@ -2539,7 +2554,7 @@ static int eval3(char **arg, typval_T *rettv, evalarg_T *const evalarg)
|
|||||||
while (p[0] == '&' && p[1] == '&') {
|
while (p[0] == '&' && p[1] == '&') {
|
||||||
// Get the second variable.
|
// Get the second variable.
|
||||||
*arg = skipwhite(*arg + 2);
|
*arg = skipwhite(*arg + 2);
|
||||||
evalarg_used->eval_flags = result ? orig_flags : orig_flags & ~EVAL_EVALUATE;
|
evalarg_used->eval_flags = result ? orig_flags : (orig_flags & ~EVAL_EVALUATE);
|
||||||
typval_T var2;
|
typval_T var2;
|
||||||
if (eval4(arg, &var2, evalarg_used) == FAIL) {
|
if (eval4(arg, &var2, evalarg_used) == FAIL) {
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
@ -4201,3 +4201,34 @@ const char *tv_get_string_buf(const typval_T *const tv, char *const buf)
|
|||||||
|
|
||||||
return res != NULL ? res : "";
|
return res != NULL ? res : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return true when "tv" is not falsy: non-zero, non-empty string, non-empty
|
||||||
|
/// list, etc. Mostly like what JavaScript does, except that empty list and
|
||||||
|
/// empty dictionary are false.
|
||||||
|
bool tv2bool(const typval_T *const tv)
|
||||||
|
{
|
||||||
|
switch (tv->v_type) {
|
||||||
|
case VAR_NUMBER:
|
||||||
|
return tv->vval.v_number != 0;
|
||||||
|
case VAR_FLOAT:
|
||||||
|
return tv->vval.v_float != 0.0;
|
||||||
|
case VAR_PARTIAL:
|
||||||
|
return tv->vval.v_partial != NULL;
|
||||||
|
case VAR_FUNC:
|
||||||
|
case VAR_STRING:
|
||||||
|
return tv->vval.v_string != NULL && *tv->vval.v_string != NUL;
|
||||||
|
case VAR_LIST:
|
||||||
|
return tv->vval.v_list != NULL && tv->vval.v_list->lv_len > 0;
|
||||||
|
case VAR_DICT:
|
||||||
|
return tv->vval.v_dict != NULL && tv->vval.v_dict->dv_hashtab.ht_used > 0;
|
||||||
|
case VAR_BOOL:
|
||||||
|
return tv->vval.v_bool == kBoolVarTrue;
|
||||||
|
case VAR_SPECIAL:
|
||||||
|
return tv->vval.v_special == kSpecialVarNull;
|
||||||
|
case VAR_BLOB:
|
||||||
|
return tv->vval.v_blob != NULL && tv->vval.v_blob->bv_ga.ga_len > 0;
|
||||||
|
case VAR_UNKNOWN:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@ -39,6 +39,38 @@ func Test_version()
|
|||||||
call assert_false(has('patch-9.9.1'))
|
call assert_false(has('patch-9.9.1'))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_op_trinary()
|
||||||
|
call assert_equal('yes', 1 ? 'yes' : 'no')
|
||||||
|
call assert_equal('no', 0 ? 'yes' : 'no')
|
||||||
|
call assert_equal('no', 'x' ? 'yes' : 'no')
|
||||||
|
call assert_equal('yes', '1x' ? 'yes' : 'no')
|
||||||
|
|
||||||
|
call assert_fails('echo [1] ? "yes" : "no"', 'E745:')
|
||||||
|
call assert_fails('echo {} ? "yes" : "no"', 'E728:')
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_op_falsy()
|
||||||
|
call assert_equal(v:true, v:true ?? 456)
|
||||||
|
call assert_equal(123, 123 ?? 456)
|
||||||
|
call assert_equal('yes', 'yes' ?? 456)
|
||||||
|
call assert_equal(0z00, 0z00 ?? 456)
|
||||||
|
call assert_equal([1], [1] ?? 456)
|
||||||
|
call assert_equal(#{one: 1}, #{one: 1} ?? 456)
|
||||||
|
if has('float')
|
||||||
|
call assert_equal(0.1, 0.1 ?? 456)
|
||||||
|
endif
|
||||||
|
|
||||||
|
call assert_equal(456, v:false ?? 456)
|
||||||
|
call assert_equal(456, 0 ?? 456)
|
||||||
|
call assert_equal(456, '' ?? 456)
|
||||||
|
call assert_equal(456, 0z ?? 456)
|
||||||
|
call assert_equal(456, [] ?? 456)
|
||||||
|
call assert_equal(456, {} ?? 456)
|
||||||
|
if has('float')
|
||||||
|
call assert_equal(456, 0.0 ?? 456)
|
||||||
|
endif
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_dict()
|
func Test_dict()
|
||||||
let d = {'': 'empty', 'a': 'a', 0: 'zero'}
|
let d = {'': 'empty', 'a': 'a', 0: 'zero'}
|
||||||
call assert_equal('empty', d[''])
|
call assert_equal('empty', d[''])
|
||||||
|
@ -35,9 +35,7 @@ func Test_help_tagjump()
|
|||||||
|
|
||||||
help ??
|
help ??
|
||||||
call assert_equal("help", &filetype)
|
call assert_equal("help", &filetype)
|
||||||
" *??* tag needs patch 8.2.1794
|
call assert_true(getline('.') =~ '\*??\*')
|
||||||
" call assert_true(getline('.') =~ '\*??\*')
|
|
||||||
call assert_true(getline('.') =~ '\*g??\*')
|
|
||||||
helpclose
|
helpclose
|
||||||
|
|
||||||
help :?
|
help :?
|
||||||
|
Loading…
Reference in New Issue
Block a user