Compare commits

...

7 Commits

Author SHA1 Message Date
Bram Moolenaar
c6e9d7063d patch 8.2.4489: failing test for comparing v:null with number
Problem:    Failing test for comparing v:null with number.
Solution:   Allow comparing v:null with number in legacy script.
            (Ken Takata, closes #9873)  Also do this for float.
2022-03-02 13:13:30 +00:00
Bram Moolenaar
f6b0c79742 patch 8.2.4488: build error with +eval but without +channel or +job
Problem:    Build error with +eval but without +channel or +job.
Solution:   Add #ifdef. (John Marriott)
2022-03-01 19:52:48 +00:00
Bram Moolenaar
7a22224875 patch 8.2.4487: Vim9: cannot compare with v:null
Problem:    Vim9: cannot compare with v:null.
Solution:   Allow comparing anything with v:null. (closes #9866)
2022-03-01 19:23:24 +00:00
Bram Moolenaar
f01af9c4e6 patch 8.2.4486: MS-Windows GUI: slow scrolling with maximized window
Problem:    MS-Windows GUI: slow scrolling with maximized window.
Solution:   Use a better way to check the window is on screen. (Ken Takata,
            closes #9865)
2022-03-01 16:02:26 +00:00
Bram Moolenaar
3e7637bd26 patch 8.2.4485: compiler warning for uninitialized variable
Problem:    Compiler warning for uninitialized variable.
Solution:   Initialize the variable. (John Marriott)
2022-02-28 21:02:19 +00:00
Bram Moolenaar
1983f1aa31 patch 8.2.4484: Vim9: some error messages are not tested
Problem:    Vim9: some error messages are not tested.
Solution:   Add a few more test cases.  Delete dead code.
2022-02-28 20:55:02 +00:00
Yegappan Lakshmanan
5de4c4372d patch 8.2.4483: command completion makes two rounds to collect matches
Problem:    Command completion makes two rounds to collect matches.
Solution:   Use a growarray to collect matches. (Yegappan Lakshmanan,
            closes #9860)
2022-02-28 13:28:38 +00:00
17 changed files with 583 additions and 252 deletions

View File

@@ -2814,38 +2814,39 @@ ExpandBufnames(
}
}
if (p != NULL)
{
if (round == 1)
++count;
else
{
if (options & WILD_HOME_REPLACE)
p = home_replace_save(buf, p);
else
p = vim_strsave(p);
if (p == NULL)
continue;
if (!fuzzy)
{
if (round == 1)
{
++count;
continue;
}
if (options & WILD_HOME_REPLACE)
p = home_replace_save(buf, p);
else
p = vim_strsave(p);
if (!fuzzy)
{
#ifdef FEAT_VIMINFO
if (matches != NULL)
{
matches[count].buf = buf;
matches[count].match = p;
count++;
}
else
#endif
(*file)[count++] = p;
}
else
{
fuzmatch[count].idx = count;
fuzmatch[count].str = p;
fuzmatch[count].score = score;
count++;
}
if (matches != NULL)
{
matches[count].buf = buf;
matches[count].match = p;
count++;
}
else
#endif
(*file)[count++] = p;
}
else
{
fuzmatch[count].idx = count;
fuzmatch[count].str = p;
fuzmatch[count].score = score;
count++;
}
}
if (count == 0) // no match found, break here

View File

@@ -2633,116 +2633,134 @@ ExpandGeneric(
int escaped)
{
int i;
int count = 0;
int round;
garray_T ga;
char_u *str;
fuzmatch_str_T *fuzmatch = NULL;
int score = 0;
int score = 0;
int fuzzy;
int funcsort = FALSE;
int match;
fuzzy = cmdline_fuzzy_complete(pat);
*matches = NULL;
*numMatches = 0;
// do this loop twice:
// round == 0: count the number of matching names
// round == 1: copy the matching names into allocated memory
for (round = 0; round <= 1; ++round)
if (!fuzzy)
ga_init2(&ga, sizeof(char *), 30);
else
ga_init2(&ga, sizeof(fuzmatch_str_T), 30);
for (i = 0; ; ++i)
{
for (i = 0; ; ++i)
{
str = (*func)(xp, i);
if (str == NULL) // end of list
break;
if (*str == NUL) // skip empty strings
continue;
str = (*func)(xp, i);
if (str == NULL) // end of list
break;
if (*str == NUL) // skip empty strings
continue;
if (xp->xp_pattern[0] != NUL)
{
if (!fuzzy)
match = vim_regexec(regmatch, str, (colnr_T)0);
match = vim_regexec(regmatch, str, (colnr_T)0);
else
{
score = fuzzy_match_str(str, pat);
match = (score != 0);
}
if (!match)
continue;
if (round)
{
if (escaped)
str = vim_strsave_escaped(str, (char_u *)" \t\\.");
else
str = vim_strsave(str);
if (str == NULL)
{
if (fuzzy)
fuzmatch_str_free(fuzmatch, count);
else if (count > 0)
FreeWild(count, *matches);
*numMatches = 0;
*matches = NULL;
return FAIL;
}
if (fuzzy)
{
fuzmatch[count].idx = count;
fuzmatch[count].str = str;
fuzmatch[count].score = score;
}
else
(*matches)[count] = str;
# ifdef FEAT_MENU
if (func == get_menu_names && str != NULL)
{
// test for separator added by get_menu_names()
str += STRLEN(str) - 1;
if (*str == '\001')
*str = '.';
}
# endif
}
++count;
}
if (round == 0)
else
match = TRUE;
if (!match)
continue;
if (escaped)
str = vim_strsave_escaped(str, (char_u *)" \t\\.");
else
str = vim_strsave(str);
if (str == NULL)
{
if (count == 0)
return OK;
if (fuzzy)
fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
else
*matches = ALLOC_MULT(char_u *, count);
if ((!fuzzy && (*matches == NULL))
|| (fuzzy && (fuzmatch == NULL)))
if (!fuzzy)
{
*numMatches = 0;
*matches = NULL;
ga_clear_strings(&ga);
return FAIL;
}
*numMatches = count;
count = 0;
for (i = 0; i < ga.ga_len; ++i)
{
fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[i];
vim_free(fuzmatch->str);
}
ga_clear(&ga);
return FAIL;
}
if (ga_grow(&ga, 1) == FAIL)
{
vim_free(str);
break;
}
if (fuzzy)
{
fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
fuzmatch->idx = ga.ga_len;
fuzmatch->str = str;
fuzmatch->score = score;
}
else
((char_u **)ga.ga_data)[ga.ga_len] = str;
# ifdef FEAT_MENU
if (func == get_menu_names)
{
// test for separator added by get_menu_names()
str += STRLEN(str) - 1;
if (*str == '\001')
*str = '.';
}
# endif
++ga.ga_len;
}
if (ga.ga_len == 0)
return OK;
// Sort the results. Keep menu's in the specified order.
if (xp->xp_context != EXPAND_MENUNAMES && xp->xp_context != EXPAND_MENUS)
if (!fuzzy && xp->xp_context != EXPAND_MENUNAMES
&& xp->xp_context != EXPAND_MENUS)
{
if (xp->xp_context == EXPAND_EXPRESSION
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC
|| xp->xp_context == EXPAND_DISASSEMBLE)
{
// <SNR> functions should be sorted to the end.
qsort((void *)ga.ga_data, (size_t)ga.ga_len, sizeof(char_u *),
sort_func_compare);
else
sort_strings((char_u **)ga.ga_data, ga.ga_len);
}
if (!fuzzy)
{
*matches = ga.ga_data;
*numMatches = ga.ga_len;
}
else
{
int funcsort = FALSE;
if (xp->xp_context == EXPAND_EXPRESSION
|| xp->xp_context == EXPAND_FUNCTIONS
|| xp->xp_context == EXPAND_USER_FUNC
|| xp->xp_context == EXPAND_DISASSEMBLE)
// <SNR> functions should be sorted to the end.
funcsort = TRUE;
if (!fuzzy)
qsort((void *)*matches, (size_t)*numMatches, sizeof(char_u *),
sort_func_compare);
}
else
{
if (!fuzzy)
sort_strings(*matches, *numMatches);
}
if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
funcsort) == FAIL)
return FAIL;
*numMatches = ga.ga_len;
}
#if defined(FEAT_SYN_HL)
@@ -2751,10 +2769,6 @@ ExpandGeneric(
reset_expand_highlight();
#endif
if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count,
funcsort) == FAIL)
return FAIL;
return OK;
}
@@ -2989,13 +3003,12 @@ ExpandUserDefined(
garray_T ga;
int fuzzy;
int match;
int score;
int count = 0;
int score = 0;
fuzzy = cmdline_fuzzy_complete(pat);
*matches = NULL;
*numMatches = 0;
retstr = call_user_expand_func(call_func_retstr, xp);
if (retstr == NULL)
return FAIL;
@@ -3013,7 +3026,7 @@ ExpandUserDefined(
keep = *e;
*e = NUL;
if (xp->xp_pattern[0] || fuzzy)
if (xp->xp_pattern[0] != NUL)
{
if (!fuzzy)
match = vim_regexec(regmatch, s, (colnr_T)0);
@@ -3038,12 +3051,11 @@ ExpandUserDefined(
{
fuzmatch_str_T *fuzmatch =
&((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
fuzmatch->idx = count;
fuzmatch->idx = ga.ga_len;
fuzmatch->str = vim_strnsave(s, e - s);
fuzmatch->score = score;
}
++ga.ga_len;
count++;
}
if (*e != NUL)
@@ -3051,6 +3063,9 @@ ExpandUserDefined(
}
vim_free(retstr);
if (ga.ga_len == 0)
return OK;
if (!fuzzy)
{
*matches = ga.ga_data;
@@ -3058,10 +3073,10 @@ ExpandUserDefined(
}
else
{
if (fuzzymatches_to_strmatches(ga.ga_data, matches, count,
FALSE) == FAIL)
if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
FALSE) == FAIL)
return FAIL;
*numMatches = count;
*numMatches = ga.ga_len;
}
return OK;
}

View File

@@ -2816,29 +2816,33 @@ eval_variable(
}
// If a list or dict variable wasn't initialized, do it now.
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL)
// Not for global variables, they are not declared.
if (ht != &globvarht)
{
tv->vval.v_dict = dict_alloc();
if (tv->vval.v_dict != NULL)
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL)
{
++tv->vval.v_dict->dv_refcount;
tv->vval.v_dict->dv_type = alloc_type(type);
tv->vval.v_dict = dict_alloc();
if (tv->vval.v_dict != NULL)
{
++tv->vval.v_dict->dv_refcount;
tv->vval.v_dict->dv_type = alloc_type(type);
}
}
}
else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL)
{
tv->vval.v_list = list_alloc();
if (tv->vval.v_list != NULL)
else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL)
{
++tv->vval.v_list->lv_refcount;
tv->vval.v_list->lv_type = alloc_type(type);
tv->vval.v_list = list_alloc();
if (tv->vval.v_list != NULL)
{
++tv->vval.v_list->lv_refcount;
tv->vval.v_list->lv_type = alloc_type(type);
}
}
else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL)
{
tv->vval.v_blob = blob_alloc();
if (tv->vval.v_blob != NULL)
++tv->vval.v_blob->bv_refcount;
}
}
else if (tv->v_type == VAR_BLOB && tv->vval.v_blob == NULL)
{
tv->vval.v_blob = blob_alloc();
if (tv->vval.v_blob != NULL)
++tv->vval.v_blob->bv_refcount;
}
copy_tv(tv, rettv);
}

View File

@@ -3016,7 +3016,7 @@ is_point_onscreen(int x, int y)
}
/*
* Check if the whole area of the specified window is on-screen.
* Check if the whole client area of the specified window is on-screen.
*
* Note about DirectX: Windows 10 1809 or above no longer maintains image of
* the window portion that is off-screen. Scrolling by DWriteContext_Scroll()
@@ -3026,16 +3026,23 @@ is_point_onscreen(int x, int y)
is_window_onscreen(HWND hwnd)
{
RECT rc;
POINT p1, p2;
GetWindowRect(hwnd, &rc);
GetClientRect(hwnd, &rc);
p1.x = rc.left;
p1.y = rc.top;
p2.x = rc.right - 1;
p2.y = rc.bottom - 1;
ClientToScreen(hwnd, &p1);
ClientToScreen(hwnd, &p2);
if (!is_point_onscreen(rc.left, rc.top))
if (!is_point_onscreen(p1.x, p1.y))
return FALSE;
if (!is_point_onscreen(rc.left, rc.bottom))
if (!is_point_onscreen(p1.x, p2.y))
return FALSE;
if (!is_point_onscreen(rc.right, rc.top))
if (!is_point_onscreen(p2.x, p1.y))
return FALSE;
if (!is_point_onscreen(rc.right, rc.bottom))
if (!is_point_onscreen(p2.x, p2.y))
return FALSE;
return TRUE;
}

201
src/map.c
View File

@@ -1263,15 +1263,15 @@ ExpandMappings(
char_u ***matches)
{
mapblock_T *mp;
garray_T ga;
int hash;
int count;
int round;
char_u *p;
int i;
int fuzzy;
int match;
int score;
fuzmatch_str_T *fuzmatch = NULL;
fuzmatch_str_T *fuzmatch;
fuzzy = cmdline_fuzzy_complete(pat);
@@ -1280,32 +1280,78 @@ ExpandMappings(
*numMatches = 0; // return values in case of FAIL
*matches = NULL;
// round == 1: Count the matches.
// round == 2: Build the array to keep the matches.
for (round = 1; round <= 2; ++round)
{
count = 0;
if (!fuzzy)
ga_init2(&ga, sizeof(char *), 3);
else
ga_init2(&ga, sizeof(fuzmatch_str_T), 3);
// First search in map modifier arguments
for (i = 0; i < 7; ++i)
{
if (i == 0)
p = (char_u *)"<silent>";
else if (i == 1)
p = (char_u *)"<unique>";
// First search in map modifier arguments
for (i = 0; i < 7; ++i)
{
if (i == 0)
p = (char_u *)"<silent>";
else if (i == 1)
p = (char_u *)"<unique>";
#ifdef FEAT_EVAL
else if (i == 2)
p = (char_u *)"<script>";
else if (i == 3)
p = (char_u *)"<expr>";
else if (i == 2)
p = (char_u *)"<script>";
else if (i == 3)
p = (char_u *)"<expr>";
#endif
else if (i == 4 && !expand_buffer)
p = (char_u *)"<buffer>";
else if (i == 5)
p = (char_u *)"<nowait>";
else if (i == 6)
p = (char_u *)"<special>";
else
else if (i == 4 && !expand_buffer)
p = (char_u *)"<buffer>";
else if (i == 5)
p = (char_u *)"<nowait>";
else if (i == 6)
p = (char_u *)"<special>";
else
continue;
if (!fuzzy)
match = vim_regexec(regmatch, p, (colnr_T)0);
else
{
score = fuzzy_match_str(p, pat);
match = (score != 0);
}
if (!match)
continue;
if (ga_grow(&ga, 1) == FAIL)
break;
if (fuzzy)
{
fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
fuzmatch->idx = ga.ga_len;
fuzmatch->str = vim_strsave(p);
fuzmatch->score = score;
}
else
((char_u **)ga.ga_data)[ga.ga_len] = vim_strsave(p);
++ga.ga_len;
}
for (hash = 0; hash < 256; ++hash)
{
if (expand_isabbrev)
{
if (hash > 0) // only one abbrev list
break; // for (hash)
mp = first_abbr;
}
else if (expand_buffer)
mp = curbuf->b_maphash[hash];
else
mp = maphash[hash];
for (; mp; mp = mp->m_next)
{
if (!(mp->m_mode & expand_mapmodes))
continue;
p = translate_mapping(mp->m_keys);
if (p == NULL)
continue;
if (!fuzzy)
@@ -1317,95 +1363,48 @@ ExpandMappings(
}
if (!match)
{
vim_free(p);
continue;
if (round == 2)
{
if (fuzzy)
{
fuzmatch[count].idx = count;
fuzmatch[count].str = vim_strsave(p);
fuzmatch[count].score = score;
}
else
(*matches)[count] = vim_strsave(p);
}
++count;
}
for (hash = 0; hash < 256; ++hash)
{
if (expand_isabbrev)
if (ga_grow(&ga, 1) == FAIL)
{
if (hash > 0) // only one abbrev list
break; // for (hash)
mp = first_abbr;
vim_free(p);
break;
}
else if (expand_buffer)
mp = curbuf->b_maphash[hash];
else
mp = maphash[hash];
for (; mp; mp = mp->m_next)
{
if (mp->m_mode & expand_mapmodes)
{
p = translate_mapping(mp->m_keys);
if (p != NULL)
{
if (!fuzzy)
match = vim_regexec(regmatch, p, (colnr_T)0);
else
{
score = fuzzy_match_str(p, pat);
match = (score != 0);
}
if (match)
{
if (round == 2)
{
if (fuzzy)
{
fuzmatch[count].idx = count;
fuzmatch[count].str = p;
fuzmatch[count].score = score;
}
else
(*matches)[count] = p;
p = NULL;
}
++count;
}
}
vim_free(p);
}
} // for (mp)
} // for (hash)
if (count == 0) // no match found
break; // for (round)
if (round == 1)
{
if (fuzzy)
{
fuzmatch = ALLOC_MULT(fuzmatch_str_T, count);
if (fuzmatch == NULL)
return FAIL;
fuzmatch = &((fuzmatch_str_T *)ga.ga_data)[ga.ga_len];
fuzmatch->idx = ga.ga_len;
fuzmatch->str = p;
fuzmatch->score = score;
}
else
{
*matches = ALLOC_MULT(char_u *, count);
if (*matches == NULL)
return FAIL;
}
}
} // for (round)
((char_u **)ga.ga_data)[ga.ga_len] = p;
if (fuzzy && fuzzymatches_to_strmatches(fuzmatch, matches, count,
FALSE) == FAIL)
++ga.ga_len;
} // for (mp)
} // for (hash)
if (ga.ga_len == 0)
return FAIL;
if (!fuzzy)
{
*matches = ga.ga_data;
*numMatches = ga.ga_len;
}
else
{
if (fuzzymatches_to_strmatches(ga.ga_data, matches, ga.ga_len,
FALSE) == FAIL)
return FAIL;
*numMatches = ga.ga_len;
}
count = *numMatches;
if (count > 1)
{
char_u **ptr1;

View File

@@ -57,6 +57,7 @@ int tv_check_lock(typval_T *tv, char_u *name, int use_gettext);
void copy_tv(typval_T *from, typval_T *to);
int typval_compare(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic);
int typval_compare_list(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
int typval_compare_null(typval_T *tv1, typval_T *tv2);
int typval_compare_blob(typval_T *tv1, typval_T *tv2, exprtype_T type, int *res);
int typval_compare_dict(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);
int typval_compare_func(typval_T *tv1, typval_T *tv2, exprtype_T type, int ic, int *res);

View File

@@ -2661,6 +2661,24 @@ func Test_fuzzy_completion_userdefined_func()
set wildoptions&
endfunc
" <SNR> functions should be sorted to the end
func Test_fuzzy_completion_userdefined_snr_func()
func s:Sendmail()
endfunc
func SendSomemail()
endfunc
func S1e2n3dmail()
endfunc
set wildoptions=fuzzy
call feedkeys(":call sendmail\<C-A>\<C-B>\"\<CR>", 'tx')
call assert_equal('"call SendSomemail() S1e2n3dmail() '
\ .. expand("<SID>") .. 'Sendmail()', @:)
set wildoptions&
delfunc s:Sendmail
delfunc SendSomemail
delfunc S1e2n3dmail
endfunc
" user defined command name completion
func Test_fuzzy_completion_userdefined_cmd()
set wildoptions&

View File

@@ -550,6 +550,13 @@ def Test_assign_index()
bl[-2] = 0x66
assert_equal(0z77226644, bl)
lines =<< trim END
g:val = '22'
var bl = 0z11
bl[1] = g:val
END
v9.CheckDefExecAndScriptFailure(lines, 'E1030: Using a String as a Number: "22"')
# should not read the next line when generating "a.b"
var a = {}
a.b = {}
@@ -1233,12 +1240,18 @@ def Test_script_var_default()
var lines =<< trim END
vim9script
var l: list<number>
var li = [1, 2]
var bl: blob
var bli = 0z12
var d: dict<number>
var di = {'a': 1, 'b': 2}
def Echo()
assert_equal([], l)
assert_equal([1, 2], li)
assert_equal(0z, bl)
assert_equal(0z12, bli)
assert_equal({}, d)
assert_equal({'a': 1, 'b': 2}, di)
enddef
Echo()
END
@@ -1502,6 +1515,30 @@ def Test_assign_list()
END
v9.CheckDefAndScriptSuccess(lines)
lines =<< trim END
var l = [1, 2]
g:idx = 'x'
l[g:idx : 1] = [0]
echo l
END
v9.CheckDefExecAndScriptFailure(lines, 'E1030: Using a String as a Number: "x"')
lines =<< trim END
var l = [1, 2]
g:idx = 3
l[g:idx : 1] = [0]
echo l
END
v9.CheckDefExecAndScriptFailure(lines, 'E684: list index out of range: 3')
lines =<< trim END
var l = [1, 2]
g:idx = 'y'
l[1 : g:idx] = [0]
echo l
END
v9.CheckDefExecAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "y"'])
v9.CheckDefFailure(["var l: list<number> = ['', true]"], 'E1012: Type mismatch; expected list<number> but got list<any>', 1)
v9.CheckDefFailure(["var l: list<list<number>> = [['', true]]"], 'E1012: Type mismatch; expected list<list<number>> but got list<list<any>>', 1)
enddef

View File

@@ -1846,6 +1846,8 @@ def Test_disassemble_compare()
['true != isFalse', 'COMPAREBOOL !='],
['v:none == isNull', 'COMPARESPECIAL =='],
['v:none != isNull', 'COMPARESPECIAL !='],
['"text" == isNull', 'COMPARENULL =='],
['"text" != isNull', 'COMPARENULL !='],
['111 == aNumber', 'COMPARENR =='],
['111 != aNumber', 'COMPARENR !='],

View File

@@ -712,6 +712,81 @@ def Test_expr4_equal()
unlet g:notReached
enddef
def Test_expr4_compare_null()
g:null_dict = test_null_dict()
g:not_null_list = []
var lines =<< trim END
assert_true(test_null_blob() == v:null)
assert_true(v:null == test_null_blob())
assert_false(test_null_blob() != v:null)
assert_false(v:null != test_null_blob())
if has('channel')
assert_true(test_null_channel() == v:null)
assert_true(v:null == test_null_channel())
assert_false(test_null_channel() != v:null)
assert_false(v:null != test_null_channel())
endif
assert_true(test_null_dict() == v:null)
assert_true(v:null == test_null_dict())
assert_false(test_null_dict() != v:null)
assert_false(v:null != test_null_dict())
assert_true(g:null_dict == v:null)
assert_true(v:null == g:null_dict)
assert_false(g:null_dict != v:null)
assert_false(v:null != g:null_dict)
assert_true(test_null_function() == v:null)
assert_true(v:null == test_null_function())
assert_false(test_null_function() != v:null)
assert_false(v:null != test_null_function())
if has('job')
assert_true(test_null_job() == v:null)
assert_true(v:null == test_null_job())
assert_false(test_null_job() != v:null)
assert_false(v:null != test_null_job())
endif
assert_true(test_null_list() == v:null)
assert_true(v:null == test_null_list())
assert_false(test_null_list() != v:null)
assert_false(v:null != test_null_list())
assert_false(g:not_null_list == v:null)
assert_false(v:null == g:not_null_list)
assert_true(g:not_null_list != v:null)
assert_true(v:null != g:not_null_list)
assert_true(test_null_partial() == v:null)
assert_true(v:null == test_null_partial())
assert_false(test_null_partial() != v:null)
assert_false(v:null != test_null_partial())
assert_true(test_null_string() == v:null)
assert_true(v:null == test_null_string())
assert_false(test_null_string() != v:null)
assert_false(v:null != test_null_string())
END
v9.CheckDefAndScriptSuccess(lines)
unlet g:null_dict
unlet g:not_null_list
v9.CheckDefAndScriptFailure(['echo 123 == v:null'], 'E1072: Cannot compare number with special')
v9.CheckDefAndScriptFailure(['echo v:null == 123'], 'E1072: Cannot compare special with number')
v9.CheckDefAndScriptFailure(['echo 123 != v:null'], 'E1072: Cannot compare number with special')
v9.CheckDefAndScriptFailure(['echo v:null != 123'], 'E1072: Cannot compare special with number')
v9.CheckDefAndScriptFailure(['echo true == v:null'], 'E1072: Cannot compare bool with special')
v9.CheckDefAndScriptFailure(['echo v:null == true'], 'E1072: Cannot compare special with bool')
v9.CheckDefAndScriptFailure(['echo true != v:null'], 'E1072: Cannot compare bool with special')
v9.CheckDefAndScriptFailure(['echo v:null != true'], 'E1072: Cannot compare special with bool')
v9.CheckDefAndScriptFailure(['echo false == v:null'], 'E1072: Cannot compare bool with special')
v9.CheckDefExecAndScriptFailure(['echo [] == v:none'], ['E1072: Cannot compare list with special', 'E691: Can only compare List with List'])
enddef
def Test_expr4_wrong_type()
for op in ['>', '>=', '<', '<=', '=~', '!~']
v9.CheckDefExecAndScriptFailure([
@@ -2782,6 +2857,23 @@ def Test_expr8_any_index_slice()
v9.CheckDefAndScriptSuccess(lines)
lines =<< trim END
vim9script
def PosIdx(s: string): string
return s[1]
enddef
def NegIdx(s: string): string
return s[-1]
enddef
set enc=latin1
assert_equal("\xe4", PosIdx("a\xe4\xe5"))
assert_equal("\xe5", NegIdx("a\xe4\xe5"))
set enc=utf-8
END
v9.CheckScriptSuccess(lines)
v9.CheckDefExecAndScriptFailure(['echo g:testblob[2]'], 'E979:', 1)
v9.CheckDefExecAndScriptFailure(['echo g:testblob[-3]'], 'E979:', 1)

View File

@@ -550,6 +550,44 @@ def Test_call_ufunc_count()
unlet g:counter
enddef
def Test_call_ufunc_failure()
var lines =<< trim END
vim9script
def Tryit()
g:Global(1, 2, 3)
enddef
func g:Global(a, b, c)
echo a:a a:b a:c
endfunc
defcompile
func! g:Global(a, b)
echo a:a a:b
endfunc
Tryit()
END
v9.CheckScriptFailure(lines, 'E118: Too many arguments for function: Global')
delfunc g:Global
lines =<< trim END
vim9script
g:Ref = function('len')
def Tryit()
g:Ref('x')
enddef
defcompile
g:Ref = function('add')
Tryit()
END
v9.CheckScriptFailure(lines, 'E119: Not enough arguments for function: add')
unlet g:Ref
enddef
def s:MyVarargs(arg: string, ...rest: list<string>): string
var res = arg
for s in rest

View File

@@ -6546,9 +6546,16 @@ func Test_type()
call assert_true(v:true != v:false)
call assert_true(v:null == 0)
call assert_false(v:null == 1)
call assert_false(v:null != 0)
call assert_true(v:none == 0)
call assert_false(v:none == 1)
call assert_false(v:none != 0)
if has('float')
call assert_true(v:null == 0.0)
call assert_false(v:null == 0.1)
call assert_false(v:null != 0.0)
endif
call assert_true(v:false is v:false)
call assert_true(v:true is v:true)

View File

@@ -1169,6 +1169,21 @@ typval_compare(
// it means TRUE.
n1 = (type == EXPR_ISNOT);
}
else if (((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
|| (tv2->v_type == VAR_SPECIAL
&& tv2->vval.v_number == VVAL_NULL))
&& tv1->v_type != tv2->v_type
&& (type == EXPR_EQUAL || type == EXPR_NEQUAL))
{
n1 = typval_compare_null(tv1, tv2);
if (n1 == MAYBE)
{
clear_tv(tv1);
return FAIL;
}
if (type == EXPR_NEQUAL)
n1 = !n1;
}
else if (tv1->v_type == VAR_BLOB || tv2->v_type == VAR_BLOB)
{
if (typval_compare_blob(tv1, tv2, type, &res) == FAIL)
@@ -1365,6 +1380,48 @@ typval_compare_list(
return OK;
}
/*
* Compare v:null/v:none with another type. Return TRUE if the value is NULL.
*/
int
typval_compare_null(typval_T *tv1, typval_T *tv2)
{
if ((tv1->v_type == VAR_SPECIAL && tv1->vval.v_number == VVAL_NULL)
|| (tv2->v_type == VAR_SPECIAL && tv2->vval.v_number == VVAL_NULL))
{
typval_T *tv = tv1->v_type == VAR_SPECIAL ? tv2 : tv1;
switch (tv->v_type)
{
case VAR_BLOB: return tv->vval.v_blob == NULL;
#ifdef FEAT_JOB_CHANNEL
case VAR_CHANNEL: return tv->vval.v_channel == NULL;
#endif
case VAR_DICT: return tv->vval.v_dict == NULL;
case VAR_FUNC: return tv->vval.v_string == NULL;
#ifdef FEAT_JOB_CHANNEL
case VAR_JOB: return tv->vval.v_job == NULL;
#endif
case VAR_LIST: return tv->vval.v_list == NULL;
case VAR_PARTIAL: return tv->vval.v_partial == NULL;
case VAR_STRING: return tv->vval.v_string == NULL;
case VAR_NUMBER: if (!in_vim9script())
return tv->vval.v_number == 0;
break;
#ifdef FEAT_FLOAT
case VAR_FLOAT: if (!in_vim9script())
return tv->vval.v_float == 0.0;
break;
#endif
default: break;
}
}
semsg(_(e_cannot_compare_str_with_str),
vartype_name(tv1->v_type), vartype_name(tv2->v_type));
return MAYBE;
}
/*
* Compare "tv1" to "tv2" as blobs acording to "type".
* Put the result, false or true, in "res".

View File

@@ -754,6 +754,20 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4489,
/**/
4488,
/**/
4487,
/**/
4486,
/**/
4485,
/**/
4484,
/**/
4483,
/**/
4482,
/**/

View File

@@ -134,6 +134,7 @@ typedef enum {
// comparative operations; isn_arg.op.op_type is exprtype_T, op_ic used
ISN_COMPAREBOOL,
ISN_COMPARESPECIAL,
ISN_COMPARENULL,
ISN_COMPARENR,
ISN_COMPAREFLOAT,
ISN_COMPARESTRING,

View File

@@ -1027,7 +1027,7 @@ call_by_name(
{
int func_idx = find_internal_func(name);
if (func_idx < 0)
if (func_idx < 0) // Impossible?
return FAIL;
if (check_internal_func(func_idx, argcount) < 0)
return FAIL;
@@ -1452,8 +1452,6 @@ get_split_sourceline(
char_u *p;
char_u *line;
if (*sp->nextline == NUL)
return NULL;
p = vim_strchr(sp->nextline, '\n');
if (p == NULL)
{
@@ -1911,11 +1909,11 @@ execute_storerange(isn_T *iptr, ectx_T *ectx)
else
n2 = (long)tv_get_number_chk(tv_idx2, &error);
if (error)
status = FAIL;
status = FAIL; // cannot happen?
else
{
listitem_T *li1 = check_range_index_one(
tv_dest->vval.v_list, &n1, FALSE);
tv_dest->vval.v_list, &n1, FALSE);
if (li1 == NULL)
status = FAIL;
@@ -3882,6 +3880,25 @@ exec_instructions(ectx_T *ectx)
}
break;
case ISN_COMPARENULL:
{
typval_T *tv1 = STACK_TV_BOT(-2);
typval_T *tv2 = STACK_TV_BOT(-1);
int res;
res = typval_compare_null(tv1, tv2);
if (res == MAYBE)
goto on_error;
if (iptr->isn_arg.op.op_type == EXPR_NEQUAL)
res = !res;
clear_tv(tv1);
clear_tv(tv2);
--ectx->ec_stack.ga_len;
tv1->v_type = VAR_BOOL;
tv1->vval.v_number = res ? VVAL_TRUE : VVAL_FALSE;
}
break;
// Operation with two number arguments
case ISN_OPNR:
case ISN_COMPARENR:
@@ -5903,6 +5920,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_COMPAREBOOL:
case ISN_COMPARESPECIAL:
case ISN_COMPARENULL:
case ISN_COMPARENR:
case ISN_COMPAREFLOAT:
case ISN_COMPARESTRING:
@@ -5938,6 +5956,7 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
case ISN_COMPAREBOOL: type = "COMPAREBOOL"; break;
case ISN_COMPARESPECIAL:
type = "COMPARESPECIAL"; break;
case ISN_COMPARENULL: type = "COMPARENULL"; break;
case ISN_COMPARENR: type = "COMPARENR"; break;
case ISN_COMPAREFLOAT: type = "COMPAREFLOAT"; break;
case ISN_COMPARESTRING:

View File

@@ -372,6 +372,24 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
|| ((type1 == VAR_NUMBER || type1 == VAR_FLOAT)
&& (type2 == VAR_NUMBER || type2 == VAR_FLOAT)))
isntype = ISN_COMPAREANY;
else if (type1 == VAR_SPECIAL || type2 == VAR_SPECIAL)
{
switch (type1 == VAR_SPECIAL ? type2 : type1)
{
case VAR_BLOB: break;
case VAR_CHANNEL: break;
case VAR_DICT: break;
case VAR_FUNC: break;
case VAR_JOB: break;
case VAR_LIST: break;
case VAR_PARTIAL: break;
case VAR_STRING: break;
default: semsg(_(e_cannot_compare_str_with_str),
vartype_name(type1), vartype_name(type2));
return ISN_DROP;
}
isntype = ISN_COMPARENULL;
}
if ((exprtype == EXPR_IS || exprtype == EXPR_ISNOT)
&& (isntype == ISN_COMPAREBOOL
@@ -388,7 +406,7 @@ get_compare_isn(exprtype_T exprtype, vartype_T type1, vartype_T type2)
&& (type1 == VAR_BOOL || type1 == VAR_SPECIAL
|| type2 == VAR_BOOL || type2 == VAR_SPECIAL)))
|| ((exprtype != EXPR_EQUAL && exprtype != EXPR_NEQUAL
&& exprtype != EXPR_IS && exprtype != EXPR_ISNOT
&& exprtype != EXPR_IS && exprtype != EXPR_ISNOT
&& (type1 == VAR_BLOB || type2 == VAR_BLOB
|| type1 == VAR_LIST || type2 == VAR_LIST))))
{
@@ -2131,6 +2149,7 @@ delete_instr(isn_T *isn)
case ISN_COMPAREFUNC:
case ISN_COMPARELIST:
case ISN_COMPARENR:
case ISN_COMPARENULL:
case ISN_COMPARESPECIAL:
case ISN_COMPARESTRING:
case ISN_CONCAT: