Compare commits

..

11 Commits

Author SHA1 Message Date
Bram Moolenaar
501f978288 patch 8.2.4635: tests using null list or dict fail
Problem:    Tests using null list or dict fail.
Solution:   Only use the new rules for Vim9 script.
2022-03-27 16:51:04 +01:00
Bram Moolenaar
ec15b1cfdc patch 8.2.4634: Vim9: cannot initialize a variable to null_list
Problem:    Vim9: cannot initialize a variable to null_list.
Solution:   Give negative count to NEWLIST. (closes #10027)
            Also fix inconsistencies in comparing with null values.
2022-03-27 16:29:53 +01:00
Bram Moolenaar
c75bca3ee9 patch 8.2.4633: Visual range does not work before command modifiers
Problem:    Visual range does not work before command modifiers.
Solution:   Move Visual range to after command modifiers.
2022-03-27 13:36:50 +01:00
Bram Moolenaar
f3980dc5d0 patch 8.2.4632: using freed memory in flatten()
Problem:    Using freed memory in flatten().
Solution:   Clear typval after recursing into list.
2022-03-26 16:42:23 +00:00
Bram Moolenaar
347538fad0 patch 8.2.4631: crash when switching window in BufWipeout autocommand
Problem:    Crash when switching window in BufWipeout autocommand.
Solution:   Put any buffer in the window to avoid it being NULL.
            (closes #10024)
2022-03-26 16:28:06 +00:00
Bram Moolenaar
bf269ed0b0 patch 8.2.4630: 'cursorline' not always updated with 'culopt' is "screenline"
Problem:    'cursorline' not always updated with 'cursorlineopt' is
            "screenline".
Solution:   Call check_redraw_cursorline() more often. (closes #10013)
2022-03-26 13:28:14 +00:00
Bram Moolenaar
c6c1ec4da5 patch 8.2.4629: flattennew() makes a deep copy unnecessarily
Problem:    flattennew() makes a deep copy unnecessarily.
Solution:   Use a shallow copy. (issue #10012)
2022-03-26 10:50:11 +00:00
Yegappan Lakshmanan
5e877baf87 patch 8.2.4628: not enough testing for 2/3 letter substitute commands
Problem:    Not enough testing for 2/3 letter substitute commands.
Solution:   Add more tests. (Yegappan Lakshmanan, closes #10019)
2022-03-25 21:19:26 +00:00
Bram Moolenaar
acf7d73a7f patch 8.2.4627: flatten() does not use maxdepth correctly
Problem:    flatten() does not use maxdepth correctly.
Solution:   Use a recursive implementation. (closes #10020)
2022-03-25 19:50:57 +00:00
Bram Moolenaar
abb6fbd14d patch 8.2.4626: Visual area not updated when removing sign in Visual mode
Problem:    Visual area not fully updated when removing sign in Visual mode
            while scrolling.
Solution:   Adjust check for topline. (closes #10017)
2022-03-25 15:42:27 +00:00
Bram Moolenaar
90da27b927 patch 8.2.4625: old Coverity warning for resource leak
Problem:    Old Coverity warning for resource leak.
Solution:   Call FreeWild() if expanding matches did not fail.
2022-03-25 14:54:18 +00:00
30 changed files with 717 additions and 157 deletions

View File

@@ -708,6 +708,10 @@ aucmd_abort:
*/
if (wipe_buf)
{
// Do not wipe out the buffer if it is used in a window.
if (buf->b_nwindows > 0)
return FALSE;
if (action == DOBUF_WIPE_REUSE)
{
// we can re-use this buffer number, store it

View File

@@ -1730,7 +1730,7 @@ win_update(win_T *wp)
if (mod_top != 0
&& wp->w_topline == mod_top
&& (!wp->w_lines[0].wl_valid
|| wp->w_topline <= wp->w_lines[0].wl_lnum))
|| wp->w_topline == wp->w_lines[0].wl_lnum))
{
// w_topline is the first changed line and window is not scrolled,
// the scrolling from changed lines will be done further down.

View File

@@ -1058,6 +1058,10 @@ doESCkey:
case K_COMMAND: // <Cmd>command<CR>
case K_SCRIPT_COMMAND: // <ScriptCmd>command<CR>
do_cmdkey_command(c, 0);
#ifdef FEAT_SYN_HL
// Might need to update for 'cursorline'.
check_redraw_cursorline();
#endif
#ifdef FEAT_TERMINAL
if (term_use_loop())
// Started a terminal that gets the input, exit Insert mode.

View File

@@ -2816,11 +2816,14 @@ eval_variable(
type = sv->sv_type;
}
// If a list or dict variable wasn't initialized, do it now.
// Not for global variables, they are not declared.
// If a list or dict variable wasn't initialized and has meaningful
// type, do it now. Not for global variables, they are not
// declared.
if (ht != &globvarht)
{
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL)
if (tv->v_type == VAR_DICT && tv->vval.v_dict == NULL
&& ((type != NULL && type != &t_dict_empty)
|| !in_vim9script()))
{
tv->vval.v_dict = dict_alloc();
if (tv->vval.v_dict != NULL)
@@ -2829,7 +2832,9 @@ eval_variable(
tv->vval.v_dict->dv_type = alloc_type(type);
}
}
else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL)
else if (tv->v_type == VAR_LIST && tv->vval.v_list == NULL
&& ((type != NULL && type != &t_list_empty)
|| !in_vim9script()))
{
tv->vval.v_list = list_alloc();
if (tv->vval.v_list != NULL)
@@ -2838,12 +2843,6 @@ eval_variable(
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;
}
}
copy_tv(tv, rettv);
}

View File

@@ -2782,13 +2782,25 @@ parse_command_modifiers(
cmdmod_T *cmod,
int skip_only)
{
char_u *cmd_start;
char_u *p;
int starts_with_colon = FALSE;
int vim9script = in_vim9script();
int has_visual_range = FALSE;
CLEAR_POINTER(cmod);
cmod->cmod_flags = sticky_cmdmod_flags;
if (STRNCMP(eap->cmd, "'<,'>", 5) == 0)
{
// The automatically inserted Visual area range is skipped, so that
// typing ":cmdmod cmd" in Visual mode works without having to move the
// range to after the modififiers.
eap->cmd += 5;
cmd_start = eap->cmd;
has_visual_range = TRUE;
}
// Repeat until no more command modifiers are found.
for (;;)
{
@@ -2849,12 +2861,11 @@ parse_command_modifiers(
{
char_u *s, *n;
for (s = p; ASCII_ISALPHA(*s); ++s)
for (s = eap->cmd; ASCII_ISALPHA(*s); ++s)
;
n = skipwhite(s);
if (vim_strchr((char_u *)".=", *n) != NULL
|| *s == '['
|| (*n != NUL && n[1] == '='))
if (*n == '.' || *n == '=' || (*n != NUL && n[1] == '=')
|| *s == '[')
break;
}
@@ -3081,6 +3092,17 @@ parse_command_modifiers(
break;
}
if (has_visual_range && eap->cmd > cmd_start)
{
// Move the '<,'> range to after the modifiers and insert a colon.
// Since the modifiers have been parsed put the colon on top of the
// space: "'<,'>mod cmd" -> "mod:'<,'>cmd
// Put eap->cmd after the colon.
mch_memmove(cmd_start - 5, cmd_start, eap->cmd - cmd_start);
eap->cmd -= 5;
mch_memmove(eap->cmd - 1, ":'<,'>", 6);
}
return OK;
}

View File

@@ -947,6 +947,7 @@ helptags_one(
FILE *fd_tags;
FILE *fd;
garray_T ga;
int res;
int filecount;
char_u **files;
char_u *p1, *p2;
@@ -965,12 +966,14 @@ helptags_one(
STRCPY(NameBuff, dir);
STRCAT(NameBuff, "/**/*");
STRCAT(NameBuff, ext);
if (gen_expand_wildcards(1, &NameBuff, &filecount, &files,
EW_FILE|EW_SILENT) == FAIL
|| filecount == 0)
res = gen_expand_wildcards(1, &NameBuff, &filecount, &files,
EW_FILE|EW_SILENT);
if (res == FAIL || filecount == 0)
{
if (!got_int)
semsg(_(e_no_match_str_1), NameBuff);
if (res != FAIL)
FreeWild(filecount, files);
return;
}

View File

@@ -916,59 +916,54 @@ list_assign_range(
}
/*
* Flatten "list" to depth "maxdepth".
* Flatten up to "maxitems" in "list", starting at "first" to depth "maxdepth".
* When "first" is NULL use the first item.
* It does nothing if "maxdepth" is 0.
* Returns FAIL when out of memory.
*/
static void
list_flatten(list_T *list, long maxdepth)
list_flatten(list_T *list, listitem_T *first, long maxitems, long maxdepth)
{
listitem_T *item;
listitem_T *tofree;
int n;
int done = 0;
if (maxdepth == 0)
return;
CHECK_LIST_MATERIALIZE(list);
if (first == NULL)
item = list->lv_first;
else
item = first;
n = 0;
item = list->lv_first;
while (item != NULL)
while (item != NULL && done < maxitems)
{
listitem_T *next = item->li_next;
fast_breakcheck();
if (got_int)
return;
if (item->li_tv.v_type == VAR_LIST)
{
listitem_T *next = item->li_next;
list_T *itemlist = item->li_tv.vval.v_list;
vimlist_remove(list, item, item);
if (list_extend(list, item->li_tv.vval.v_list, next) == FAIL)
if (list_extend(list, itemlist, next) == FAIL)
{
list_free_item(list, item);
return;
}
if (maxdepth > 0)
list_flatten(list, item->li_prev == NULL
? list->lv_first : item->li_prev->li_next,
itemlist->lv_len, maxdepth - 1);
clear_tv(&item->li_tv);
tofree = item;
if (item->li_prev == NULL)
item = list->lv_first;
else
item = item->li_prev->li_next;
list_free_item(list, tofree);
if (++n >= maxdepth)
{
n = 0;
item = next;
}
}
else
{
n = 0;
item = item->li_next;
list_free_item(list, item);
}
++done;
item = next;
}
}
@@ -1015,7 +1010,7 @@ flatten_common(typval_T *argvars, typval_T *rettv, int make_copy)
if (make_copy)
{
l = list_copy(l, TRUE, TRUE, get_copyID());
l = list_copy(l, FALSE, TRUE, get_copyID());
rettv->vval.v_list = l;
if (l == NULL)
return;
@@ -1031,7 +1026,7 @@ flatten_common(typval_T *argvars, typval_T *rettv, int make_copy)
++l->lv_refcount;
}
list_flatten(l, maxdepth);
list_flatten(l, NULL, l->lv_len, maxdepth);
}
/*

View File

@@ -6971,6 +6971,10 @@ nv_edit(cmdarg_T *cap)
coladvance(getviscol());
State = save_State;
}
#ifdef FEAT_SYN_HL
// Might need to update for 'cursorline'.
check_redraw_cursorline();
#endif
invoke_edit(cap, FALSE, cap->cmdchar, FALSE);
}

View File

@@ -36,8 +36,8 @@ int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
int generate_LOCKCONST(cctx_T *cctx);
int generate_OLDSCRIPT(cctx_T *cctx, isntype_T isn_type, char_u *name, int sid, type_T *type);
int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type_T *type);
int generate_NEWLIST(cctx_T *cctx, int count);
int generate_NEWDICT(cctx_T *cctx, int count);
int generate_NEWLIST(cctx_T *cctx, int count, int use_null);
int generate_NEWDICT(cctx_T *cctx, int count, int use_null);
int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);

View File

@@ -0,0 +1,8 @@
|x+0&#ffffff0|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z
| +8&&|x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| > @29
|~+0#4040ff13&| @73
|~| @73
|~| @73
|~| @73
|~| @73
|-+2#0000000&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|1|2|1| @8|A|l@1|

View File

@@ -0,0 +1,8 @@
>x+8&#ffffff0|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z
| +0&&|x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| |x|y|z| @30
|~+0#4040ff13&| @73
|~| @73
|~| @73
|~| @73
|~| @73
|-+2#0000000&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|1| @10|A|l@1|

View File

@@ -0,0 +1,8 @@
| +0#0000e05#a8a8a8255@1>f+0#0000000#ffffff0|o+0&#e0e0e08@1| | +0&#ffffff0@53
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
| +0#0000e05#a8a8a8255@1|f+0#0000000#ffffff0|o@1| @54
|-+2&&@1| |V|I|S|U|A|L| |L|I|N|E| |-@1| +0&&@14|2| @8|2|,|1| @10|3@1|%|

View File

@@ -2990,4 +2990,21 @@ func Test_closing_autocmd_window()
bwipe Xb.txt
endfunc
func Test_bufwipeout_changes_window()
" This should not crash, but we don't have any expectations about what
" happens, changing window in BufWipeout has unpredictable results.
tabedit
let g:window_id = win_getid()
topleft new
setlocal bufhidden=wipe
autocmd BufWipeout <buffer> call win_gotoid(g:window_id)
tabprevious
+tabclose
unlet g:window_id
au! BufWipeout
%bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -3284,4 +3284,17 @@ func Test_cmdline_complete_scriptnames()
set wildmenu&
endfunc
" Test for expanding 2-letter and 3-letter :substitute command arguments.
" These commands don't accept an argument.
func Test_cmdline_complete_substitute_short()
for cmd in ['sc', 'sce', 'scg', 'sci', 'scI', 'scn', 'scp', 'scl',
\ 'sgc', 'sge', 'sg', 'sgi', 'sgI', 'sgn', 'sgp', 'sgl', 'sgr',
\ 'sic', 'sie', 'si', 'siI', 'sin', 'sip', 'sir',
\ 'sIc', 'sIe', 'sIg', 'sIi', 'sI', 'sIn', 'sIp', 'sIl', 'sIr',
\ 'src', 'srg', 'sri', 'srI', 'srn', 'srp', 'srl', 'sr']
call feedkeys(':' .. cmd .. " \<Tab>\<C-B>\"\<CR>", 'tx')
call assert_equal('"' .. cmd .. " \<Tab>", @:)
endfor
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -272,5 +272,26 @@ func Test_cursorline_callback()
call delete('Xcul_timer')
endfunc
func Test_cursorline_screenline_update()
CheckScreendump
let lines =<< trim END
call setline(1, repeat('xyz ', 30))
set cursorline cursorlineopt=screenline
inoremap <F2> <Cmd>call cursor(1, 1)<CR>
END
call writefile(lines, 'Xcul_screenline')
let buf = RunVimInTerminal('-S Xcul_screenline', #{rows: 8})
call term_sendkeys(buf, "A")
call VerifyScreenDump(buf, 'Test_cursorline_screenline_1', {})
call term_sendkeys(buf, "\<F2>")
call VerifyScreenDump(buf, 'Test_cursorline_screenline_2', {})
call term_sendkeys(buf, "\<Esc>")
call StopVimInTerminal(buf)
call delete('Xcul_screenline')
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -256,6 +256,27 @@ func Test_display_scroll_at_topline()
call StopVimInTerminal(buf)
endfunc
func Test_display_scroll_update_visual()
CheckScreendump
let lines =<< trim END
set scrolloff=0
call setline(1, repeat(['foo'], 10))
call sign_define('foo', { 'text': '>' })
call sign_place(1, 'bar', 'foo', bufnr(), { 'lnum': 2 })
call sign_place(2, 'bar', 'foo', bufnr(), { 'lnum': 1 })
autocmd CursorMoved * if getcurpos()[1] == 2 | call sign_unplace('bar', { 'id': 1 }) | endif
END
call writefile(lines, 'XupdateVisual.vim')
let buf = RunVimInTerminal('-S XupdateVisual.vim', #{rows: 8, cols: 60})
call term_sendkeys(buf, "VG7kk")
call VerifyScreenDump(buf, 'Test_display_scroll_update_visual', {})
call StopVimInTerminal(buf)
call delete('XupdateVisual.vim')
endfunc
" Test for 'eob' (EndOfBuffer) item in 'fillchars'
func Test_eob_fillchars()
" default value

View File

@@ -79,6 +79,14 @@ func Test_flatten()
call add(y, x) " l:y = [2, [1, [...]]]
call assert_equal([1, 2, 1, 2], flatten(l:x, 2))
call assert_equal([2, l:x], l:y)
let l4 = [ 1, [ 11, [ 101, [ 1001 ] ] ] ]
call assert_equal(l4, flatten(deepcopy(l4), 0))
call assert_equal([1, 11, [101, [1001]]], flatten(deepcopy(l4), 1))
call assert_equal([1, 11, 101, [1001]], flatten(deepcopy(l4), 2))
call assert_equal([1, 11, 101, 1001], flatten(deepcopy(l4), 3))
call assert_equal([1, 11, 101, 1001], flatten(deepcopy(l4), 4))
call assert_equal([1, 11, 101, 1001], flatten(deepcopy(l4)))
endfunc
func Test_flattennew()
@@ -88,6 +96,14 @@ func Test_flattennew()
call assert_equal([1, 2, [3, 4], 5], flattennew(l, 1))
call assert_equal([1, [2, [3, 4]], 5], l)
let l4 = [ 1, [ 11, [ 101, [ 1001 ] ] ] ]
call assert_equal(l4, flatten(deepcopy(l4), 0))
call assert_equal([1, 11, [101, [1001]]], flattennew(l4, 1))
call assert_equal([1, 11, 101, [1001]], flattennew(l4, 2))
call assert_equal([1, 11, 101, 1001], flattennew(l4, 3))
call assert_equal([1, 11, 101, 1001], flattennew(l4, 4))
call assert_equal([1, 11, 101, 1001], flattennew(l4))
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -579,6 +579,13 @@ func Test_source_buffer_vim9()
call assert_equal(#{pi: 3.12, e: 2.71828}, g:Math)
call assert_equal(['vim', 'nano'], g:Editors)
" '<,'> range before the cmd modifier works
unlet g:Math
unlet g:Editors
exe "normal 6GV4j:vim9cmd source\<CR>"
call assert_equal(['vim', 'nano'], g:Editors)
unlet g:Editors
" test for using try/catch
%d _
let lines =<< trim END

View File

@@ -1,4 +1,4 @@
" Tests for multi-line regexps with ":s".
" Tests for the substitute (:s) command
source shared.vim
source check.vim
@@ -1000,5 +1000,287 @@ func Test_using_old_sub()
set nocompatible
endfunc
" Test for the 2-letter and 3-letter :substitute commands
func Test_substitute_short_cmd()
new
call setline(1, ['one', 'one one one'])
s/one/two
call cursor(2, 1)
" :sc
call feedkeys(":sc\<CR>y", 'xt')
call assert_equal('two one one', getline(2))
" :scg
call setline(2, 'one one one')
call feedkeys(":scg\<CR>nyq", 'xt')
call assert_equal('one two one', getline(2))
" :sci
call setline(2, 'ONE One onE')
call feedkeys(":sci\<CR>y", 'xt')
call assert_equal('two One onE', getline(2))
" :scI
set ignorecase
call setline(2, 'ONE One one')
call feedkeys(":scI\<CR>y", 'xt')
call assert_equal('ONE One two', getline(2))
set ignorecase&
" :scn
call setline(2, 'one one one')
let t = execute('scn')->split("\n")
call assert_equal(['1 match on 1 line'], t)
call assert_equal('one one one', getline(2))
" :scp
call setline(2, "\tone one one")
redir => output
call feedkeys(":scp\<CR>y", 'xt')
redir END
call assert_equal(' two one one', output->split("\n")[-1])
call assert_equal("\ttwo one one", getline(2))
" :scl
call setline(2, "\tone one one")
redir => output
call feedkeys(":scl\<CR>y", 'xt')
redir END
call assert_equal("^Itwo one one$", output->split("\n")[-1])
call assert_equal("\ttwo one one", getline(2))
" :sgc
call setline(2, 'one one one one one')
call feedkeys(":sgc\<CR>nyyq", 'xt')
call assert_equal('one two two one one', getline(2))
" :sg
call setline(2, 'one one one')
sg
call assert_equal('two two two', getline(2))
" :sgi
call setline(2, 'ONE One onE')
sgi
call assert_equal('two two two', getline(2))
" :sgI
set ignorecase
call setline(2, 'ONE One one')
sgI
call assert_equal('ONE One two', getline(2))
set ignorecase&
" :sgn
call setline(2, 'one one one')
let t = execute('sgn')->split("\n")
call assert_equal(['3 matches on 1 line'], t)
call assert_equal('one one one', getline(2))
" :sgp
call setline(2, "\tone one one")
redir => output
sgp
redir END
call assert_equal(' two two two', output->split("\n")[-1])
call assert_equal("\ttwo two two", getline(2))
" :sgl
call setline(2, "\tone one one")
redir => output
sgl
redir END
call assert_equal("^Itwo two two$", output->split("\n")[-1])
call assert_equal("\ttwo two two", getline(2))
" :sgr
call setline(2, "one one one")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
sgr
call assert_equal('xyz xyz xyz', getline(2))
" :sic
call cursor(1, 1)
s/one/two/e
call setline(2, "ONE One one")
call cursor(2, 1)
call feedkeys(":sic\<CR>y", 'xt')
call assert_equal('two One one', getline(2))
" :si
call setline(2, "ONE One one")
si
call assert_equal('two One one', getline(2))
" :siI
call setline(2, "ONE One one")
siI
call assert_equal('ONE One two', getline(2))
" :sin
call setline(2, 'ONE One onE')
let t = execute('sin')->split("\n")
call assert_equal(['1 match on 1 line'], t)
call assert_equal('ONE One onE', getline(2))
" :sip
call setline(2, "\tONE One onE")
redir => output
sip
redir END
call assert_equal(' two One onE', output->split("\n")[-1])
call assert_equal("\ttwo One onE", getline(2))
" :sir
call setline(2, "ONE One onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
sir
call assert_equal('xyz One onE', getline(2))
" :sIc
call cursor(1, 1)
s/one/two/e
call setline(2, "ONE One one")
call cursor(2, 1)
call feedkeys(":sIc\<CR>y", 'xt')
call assert_equal('ONE One two', getline(2))
" :sIg
call setline(2, "ONE one onE one")
sIg
call assert_equal('ONE two onE two', getline(2))
" :sIi
call setline(2, "ONE One one")
sIi
call assert_equal('two One one', getline(2))
" :sI
call setline(2, "ONE One one")
sI
call assert_equal('ONE One two', getline(2))
" :sIn
call setline(2, 'ONE One one')
let t = execute('sIn')->split("\n")
call assert_equal(['1 match on 1 line'], t)
call assert_equal('ONE One one', getline(2))
" :sIp
call setline(2, "\tONE One one")
redir => output
sIp
redir END
call assert_equal(' ONE One two', output->split("\n")[-1])
call assert_equal("\tONE One two", getline(2))
" :sIl
call setline(2, "\tONE onE one")
redir => output
sIl
redir END
call assert_equal("^IONE onE two$", output->split("\n")[-1])
call assert_equal("\tONE onE two", getline(2))
" :sIr
call setline(2, "ONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
sIr
call assert_equal('ONE xyz onE', getline(2))
" :src
call setline(2, "ONE one one")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
call feedkeys(":src\<CR>y", 'xt')
call assert_equal('ONE xyz one', getline(2))
" :srg
call setline(2, "one one one")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
srg
call assert_equal('xyz xyz xyz', getline(2))
" :sri
call setline(2, "ONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
sri
call assert_equal('xyz one onE', getline(2))
" :srI
call setline(2, "ONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
srI
call assert_equal('ONE xyz onE', getline(2))
" :srn
call setline(2, "ONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
let t = execute('srn')->split("\n")
call assert_equal(['1 match on 1 line'], t)
call assert_equal('ONE one onE', getline(2))
" :srp
call setline(2, "\tONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
redir => output
srp
redir END
call assert_equal(' ONE xyz onE', output->split("\n")[-1])
call assert_equal("\tONE xyz onE", getline(2))
" :srl
call setline(2, "\tONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
redir => output
srl
redir END
call assert_equal("^IONE xyz onE$", output->split("\n")[-1])
call assert_equal("\tONE xyz onE", getline(2))
" :sr
call setline(2, "ONE one onE")
call cursor(2, 1)
s/abc/xyz/e
let @/ = 'one'
sr
call assert_equal('ONE xyz onE', getline(2))
" :sce
s/abc/xyz/e
call assert_fails("sc", 'E486:')
sce
" :sge
call assert_fails("sg", 'E486:')
sge
" :sie
call assert_fails("si", 'E486:')
sie
" :sIe
call assert_fails("sI", 'E486:')
sIe
bw!
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -122,13 +122,13 @@ def Test_add_blob()
END
v9.CheckDefExecFailure(lines, 'E1131:', 2)
# Getting variable with NULL blob allocates a new blob at script level
# Getting variable with NULL blob fails
lines =<< trim END
vim9script
var b: blob = test_null_blob()
add(b, 123)
END
v9.CheckScriptSuccess(lines)
v9.CheckScriptFailure(lines, 'E1131:', 3)
enddef
def Test_add_list()

View File

@@ -439,11 +439,11 @@ if has('job')
'\d\+ STORE $\d\_s*' ..
'var dd = null_dict\_s*' ..
'\d\+ NEWDICT size 0\_s*' ..
'\d\+ NEWDICT size -1\_s*' ..
'\d\+ STORE $\d\_s*' ..
'var ll = null_list\_s*' ..
'\d\+ NEWLIST size 0\_s*' ..
'\d\+ NEWLIST size -1\_s*' ..
'\d\+ STORE $\d\_s*' ..
'var Ff = null_function\_s*' ..

View File

@@ -754,6 +754,12 @@ def Test_expr4_compare_null()
assert_false(v:null != test_null_blob())
assert_false(null != null_blob)
var nb = null_blob
assert_true(nb == null_blob)
assert_true(nb == null)
assert_true(null_blob == nb)
assert_true(null == nb)
if has('channel')
assert_true(test_null_channel() == v:null)
assert_true(null_channel == null)
@@ -763,6 +769,12 @@ def Test_expr4_compare_null()
assert_false(null_channel != null)
assert_false(v:null != test_null_channel())
assert_false(null != null_channel)
var nc = null_channel
assert_true(nc == null_channel)
assert_true(nc == null)
assert_true(null_channel == nc)
assert_true(null == nc)
endif
assert_true(test_null_dict() == v:null)
@@ -779,6 +791,12 @@ def Test_expr4_compare_null()
assert_false(g:null_dict != v:null)
assert_false(v:null != g:null_dict)
var nd = null_dict
assert_true(nd == null_dict)
assert_true(nd == null)
assert_true(null_dict == nd)
assert_true(null == nd)
assert_true(test_null_function() == v:null)
assert_true(null_function == null)
assert_true(v:null == test_null_function())
@@ -788,6 +806,12 @@ def Test_expr4_compare_null()
assert_false(v:null != test_null_function())
assert_false(null != null_function)
var Nf = null_function
assert_true(Nf == null_function)
assert_true(Nf == null)
assert_true(null_function == Nf)
assert_true(null == Nf)
if has('job')
assert_true(test_null_job() == v:null)
assert_true(null_job == null)
@@ -797,6 +821,12 @@ def Test_expr4_compare_null()
assert_false(null_job != null)
assert_false(v:null != test_null_job())
assert_false(null != null_job)
var nj = null_job
assert_true(nj == null_job)
assert_true(nj == null)
assert_true(null_job == nj)
assert_true(null == nj)
endif
assert_true(test_null_list() == v:null)
@@ -813,6 +843,12 @@ def Test_expr4_compare_null()
assert_true(g:not_null_list != v:null)
assert_true(v:null != g:not_null_list)
var nl = null_list
assert_true(nl == null_list)
assert_true(nl == null)
assert_true(null_list == nl)
assert_true(null == nl)
assert_true(test_null_partial() == v:null)
assert_true(null_partial == null)
assert_true(v:null == test_null_partial())
@@ -822,6 +858,12 @@ def Test_expr4_compare_null()
assert_false(v:null != test_null_partial())
assert_false(null != null_partial)
var Np = null_partial
assert_true(Np == null_partial)
assert_true(Np == null)
assert_true(null_partial == Np)
assert_true(null == Np)
assert_true(test_null_string() == v:null)
assert_true(null_string == null)
assert_true(v:null == test_null_string())
@@ -837,6 +879,12 @@ def Test_expr4_compare_null()
assert_false(null_string isnot test_null_string())
assert_true(null_string isnot '')
assert_true('' isnot null_string)
var ns = null_string
assert_true(ns == null_string)
assert_true(ns == null)
assert_true(null_string == ns)
assert_true(null == ns)
END
v9.CheckDefAndScriptSuccess(lines)
unlet g:null_dict

View File

@@ -1314,6 +1314,19 @@ typval_compare(
return FAIL;
}
}
#ifdef FEAT_JOB_CHANNEL
else if (tv1->v_type == tv2->v_type
&& (tv1->v_type == VAR_CHANNEL || tv1->v_type == VAR_JOB)
&& (type == EXPR_NEQUAL || type == EXPR_EQUAL))
{
if (tv1->v_type == VAR_CHANNEL)
n1 = tv1->vval.v_channel == tv2->vval.v_channel;
else
n1 = tv1->vval.v_job == tv2->vval.v_job;
if (type == EXPR_NEQUAL)
n1 = !n1;
}
#endif
else
{
if (typval_compare_string(tv1, tv2, type, ic, &res) == FAIL)
@@ -1417,7 +1430,7 @@ typval_compare_null(typval_T *tv1, typval_T *tv2)
default: break;
}
}
// although comparing null with number, float or bool is not very usefule
// although comparing null with number, float or bool is not very useful
// we won't give an error
return FALSE;
}

View File

@@ -750,6 +750,28 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4635,
/**/
4634,
/**/
4633,
/**/
4632,
/**/
4631,
/**/
4630,
/**/
4629,
/**/
4628,
/**/
4627,
/**/
4626,
/**/
4625,
/**/
4624,
/**/

View File

@@ -90,7 +90,9 @@ typedef enum {
ISN_PUSHCHANNEL, // push NULL channel
ISN_PUSHJOB, // push NULL job
ISN_NEWLIST, // push list from stack items, size is isn_arg.number
// -1 for null_list
ISN_NEWDICT, // push dict from stack items, size is isn_arg.number
// -1 for null_dict
ISN_NEWPARTIAL, // push NULL partial
ISN_AUTOLOAD, // get item from autoload import, function or variable

View File

@@ -1955,7 +1955,7 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
generate_PUSHS(cctx, &li->li_tv.vval.v_string);
li->li_tv.vval.v_string = NULL;
}
generate_NEWLIST(cctx, l->lv_len);
generate_NEWLIST(cctx, l->lv_len, FALSE);
}
list_free(l);
p += STRLEN(p);
@@ -2239,10 +2239,10 @@ compile_assignment(char_u *arg, exarg_T *eap, cmdidx_T cmdidx, cctx_T *cctx)
generate_PUSHFUNC(cctx, NULL, &t_func_void);
break;
case VAR_LIST:
generate_NEWLIST(cctx, 0);
generate_NEWLIST(cctx, 0, FALSE);
break;
case VAR_DICT:
generate_NEWDICT(cctx, 0);
generate_NEWDICT(cctx, 0, FALSE);
break;
case VAR_JOB:
generate_PUSHJOB(cctx);

View File

@@ -122,29 +122,103 @@ ufunc_argcount(ufunc_T *ufunc)
/*
* Create a new list from "count" items at the bottom of the stack.
* When "count" is zero an empty list is added to the stack.
* When "count" is -1 a NULL list is added to the stack.
*/
static int
exe_newlist(int count, ectx_T *ectx)
{
list_T *list = list_alloc_with_items(count);
list_T *list = NULL;
int idx;
typval_T *tv;
if (list == NULL)
return FAIL;
for (idx = 0; idx < count; ++idx)
list_set_item(list, idx, STACK_TV_BOT(idx - count));
if (count >= 0)
{
list = list_alloc_with_items(count);
if (list == NULL)
return FAIL;
for (idx = 0; idx < count; ++idx)
list_set_item(list, idx, STACK_TV_BOT(idx - count));
}
if (count > 0)
ectx->ec_stack.ga_len -= count - 1;
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
{
list_unref(list);
return FAIL;
}
else
++ectx->ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
tv->v_type = VAR_LIST;
tv->vval.v_list = list;
++list->lv_refcount;
if (list != NULL)
++list->lv_refcount;
return OK;
}
/*
* Implementation of ISN_NEWDICT.
* Returns FAIL on total failure, MAYBE on error.
*/
static int
exe_newdict(int count, ectx_T *ectx)
{
dict_T *dict = NULL;
dictitem_T *item;
char_u *key;
int idx;
typval_T *tv;
if (count >= 0)
{
dict = dict_alloc();
if (unlikely(dict == NULL))
return FAIL;
for (idx = 0; idx < count; ++idx)
{
// have already checked key type is VAR_STRING
tv = STACK_TV_BOT(2 * (idx - count));
// check key is unique
key = tv->vval.v_string == NULL
? (char_u *)"" : tv->vval.v_string;
item = dict_find(dict, key, -1);
if (item != NULL)
{
semsg(_(e_duplicate_key_in_dicitonary), key);
dict_unref(dict);
return MAYBE;
}
item = dictitem_alloc(key);
clear_tv(tv);
if (unlikely(item == NULL))
{
dict_unref(dict);
return FAIL;
}
item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
item->di_tv.v_lock = 0;
if (dict_add(dict, item) == FAIL)
{
// can this ever happen?
dict_unref(dict);
return FAIL;
}
}
}
if (count > 0)
ectx->ec_stack.ga_len -= 2 * count - 1;
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
return FAIL;
else
++ectx->ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
tv->v_type = VAR_DICT;
tv->v_lock = 0;
tv->vval.v_dict = dict;
if (dict != NULL)
++dict->dv_refcount;
return OK;
}
@@ -3357,57 +3431,14 @@ exec_instructions(ectx_T *ectx)
// create a dict from items on the stack
case ISN_NEWDICT:
{
int count = iptr->isn_arg.number;
dict_T *dict = dict_alloc();
dictitem_T *item;
char_u *key;
int idx;
int res;
if (unlikely(dict == NULL))
SOURCING_LNUM = iptr->isn_lnum;
res = exe_newdict(iptr->isn_arg.number, ectx);
if (res == FAIL)
goto theend;
for (idx = 0; idx < count; ++idx)
{
// have already checked key type is VAR_STRING
tv = STACK_TV_BOT(2 * (idx - count));
// check key is unique
key = tv->vval.v_string == NULL
? (char_u *)"" : tv->vval.v_string;
item = dict_find(dict, key, -1);
if (item != NULL)
{
SOURCING_LNUM = iptr->isn_lnum;
semsg(_(e_duplicate_key_in_dicitonary), key);
dict_unref(dict);
goto on_error;
}
item = dictitem_alloc(key);
clear_tv(tv);
if (unlikely(item == NULL))
{
dict_unref(dict);
goto theend;
}
item->di_tv = *STACK_TV_BOT(2 * (idx - count) + 1);
item->di_tv.v_lock = 0;
if (dict_add(dict, item) == FAIL)
{
// can this ever happen?
dict_unref(dict);
goto theend;
}
}
if (count > 0)
ectx->ec_stack.ga_len -= 2 * count - 1;
else if (GA_GROW_FAILS(&ectx->ec_stack, 1))
goto theend;
else
++ectx->ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
tv->v_type = VAR_DICT;
tv->v_lock = 0;
tv->vval.v_dict = dict;
++dict->dv_refcount;
if (res == MAYBE)
goto on_error;
}
break;

View File

@@ -958,7 +958,7 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
*arg = p;
ppconst->pp_is_const = is_all_const;
return generate_NEWLIST(cctx, count);
return generate_NEWLIST(cctx, count, FALSE);
}
/*
@@ -1246,7 +1246,7 @@ compile_dict(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
dict_unref(d);
ppconst->pp_is_const = is_all_const;
return generate_NEWDICT(cctx, count);
return generate_NEWDICT(cctx, count, FALSE);
failret:
if (*arg == NULL)

View File

@@ -581,12 +581,12 @@ generate_tv_PUSH(cctx_T *cctx, typval_T *tv)
case VAR_LIST:
if (tv->vval.v_list != NULL)
iemsg("non-empty list constant not supported");
generate_NEWLIST(cctx, 0);
generate_NEWLIST(cctx, 0, TRUE);
break;
case VAR_DICT:
if (tv->vval.v_dict != NULL)
iemsg("non-empty dict constant not supported");
generate_NEWDICT(cctx, 0);
generate_NEWDICT(cctx, 0, TRUE);
break;
#ifdef FEAT_JOB_CHANNEL
case VAR_JOB:
@@ -1115,10 +1115,11 @@ generate_VIM9SCRIPT(
}
/*
* Generate an ISN_NEWLIST instruction.
* Generate an ISN_NEWLIST instruction for "count" items.
* "use_null" is TRUE for null_list.
*/
int
generate_NEWLIST(cctx_T *cctx, int count)
generate_NEWLIST(cctx_T *cctx, int count, int use_null)
{
isn_T *isn;
type_T *member_type;
@@ -1128,7 +1129,7 @@ generate_NEWLIST(cctx_T *cctx, int count)
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_NEWLIST)) == NULL)
return FAIL;
isn->isn_arg.number = count;
isn->isn_arg.number = use_null ? -1 : count;
// Get the member type and the declared member type from all the items on
// the stack.
@@ -1145,9 +1146,10 @@ generate_NEWLIST(cctx_T *cctx, int count)
/*
* Generate an ISN_NEWDICT instruction.
* "use_null" is TRUE for null_dict.
*/
int
generate_NEWDICT(cctx_T *cctx, int count)
generate_NEWDICT(cctx_T *cctx, int count, int use_null)
{
isn_T *isn;
type_T *member_type;
@@ -1157,7 +1159,7 @@ generate_NEWDICT(cctx_T *cctx, int count)
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_NEWDICT)) == NULL)
return FAIL;
isn->isn_arg.number = count;
isn->isn_arg.number = use_null ? -1 : count;
member_type = get_member_type_from_stack(count, 2, cctx);
type = get_dict_type(member_type, cctx->ctx_type_list);

View File

@@ -2285,6 +2285,41 @@ entering_window(win_T *win)
}
#endif
static void
win_init_empty(win_T *wp)
{
redraw_win_later(wp, NOT_VALID);
wp->w_lines_valid = 0;
wp->w_cursor.lnum = 1;
wp->w_curswant = wp->w_cursor.col = 0;
wp->w_cursor.coladd = 0;
wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1
wp->w_pcmark.col = 0;
wp->w_prev_pcmark.lnum = 0;
wp->w_prev_pcmark.col = 0;
wp->w_topline = 1;
#ifdef FEAT_DIFF
wp->w_topfill = 0;
#endif
wp->w_botline = 2;
#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
wp->w_s = &wp->w_buffer->b_s;
#endif
#ifdef FEAT_TERMINAL
term_reset_wincolor(wp);
#endif
}
/*
* Init the current window "curwin".
* Called when a new file is being edited.
*/
void
curwin_init(void)
{
win_init_empty(curwin);
}
/*
* Close all windows for buffer "buf".
*/
@@ -2786,7 +2821,17 @@ win_close_othertab(win_T *win, int free_buf, tabpage_T *tp)
for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next)
;
if (ptp == NULL || tp == curtab)
{
// If the buffer was removed from the window we have to give it any
// buffer.
if (win_valid_any_tab(win) && win->w_buffer == NULL)
{
win->w_buffer = firstbuf;
++firstbuf->b_nwindows;
win_init_empty(win);
}
return;
}
// Autocommands may have closed the window already.
for (wp = tp->tp_firstwin; wp != NULL && wp != win; wp = wp->w_next)
@@ -3685,41 +3730,6 @@ close_others(
emsg(_(e_other_window_contains_changes));
}
static void
win_init_empty(win_T *wp)
{
redraw_win_later(wp, NOT_VALID);
wp->w_lines_valid = 0;
wp->w_cursor.lnum = 1;
wp->w_curswant = wp->w_cursor.col = 0;
wp->w_cursor.coladd = 0;
wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1
wp->w_pcmark.col = 0;
wp->w_prev_pcmark.lnum = 0;
wp->w_prev_pcmark.col = 0;
wp->w_topline = 1;
#ifdef FEAT_DIFF
wp->w_topfill = 0;
#endif
wp->w_botline = 2;
#if defined(FEAT_SYN_HL) || defined(FEAT_SPELL)
wp->w_s = &wp->w_buffer->b_s;
#endif
#ifdef FEAT_TERMINAL
term_reset_wincolor(wp);
#endif
}
/*
* Init the current window "curwin".
* Called when a new file is being edited.
*/
void
curwin_init(void)
{
win_init_empty(curwin);
}
/*
* Allocate the first window and put an empty buffer in it.
* Called from main().