Merge remote-tracking branch 'vim/master'

This commit is contained in:
ichizok
2022-09-08 12:41:30 +09:00
29 changed files with 379 additions and 71 deletions
+20 -2
View File
@@ -6239,8 +6239,26 @@ min({expr}) Return the minimum value of all items in {expr}. Example: >
mkdir({name} [, {path} [, {prot}]])
Create directory {name}.
If {path} is "p" then intermediate directories are created as
necessary. Otherwise it must be "".
If {path} contains "p" then intermediate directories are
created as necessary. Otherwise it must be "".
If {path} contains "D" then {name} is deleted at the end of
the current function, as with: >
defer delete({name}, 'd')
<
If {path} contains "R" then {name} is deleted recursively at
the end of the current function, as with: >
defer delete({name}, 'rf')
< Note that when {name} has more than one part and "p" is used
some directories may already exist. Only the first one that
is created and what it contains is scheduled to be deleted.
E.g. when using: >
call mkdir('subdir/tmp/autoload', 'pR')
< and "subdir" already exists then "subdir/tmp" will be
scheduled for deletion, like with: >
defer delete('subdir/tmp', 'rf')
< Note that if scheduling the defer fails the directory is not
deleted. This should only happen when out of memory.
If {prot} is given it is used to set the protection bits of
the new directory. The default is 0o755 (rwxr-xr-x: r/w for
+1 -1
View File
@@ -940,7 +940,7 @@ au BufNewFile,BufRead *.java,*.jav setf java
au BufNewFile,BufRead *.jj,*.jjt setf javacc
" JavaScript, ECMAScript, ES module script, CommonJS script
au BufNewFile,BufRead *.js,*.javascript,*.es,*.mjs,*.cjs setf javascript
au BufNewFile,BufRead *.js,*.jsm,*.javascript,*.es,*.mjs,*.cjs setf javascript
" JavaScript with React
au BufNewFile,BufRead *.jsx setf javascriptreact
+1 -2
View File
@@ -943,14 +943,13 @@ vim_isprintc_strict(int c)
init_chartabsize_arg(
chartabsize_T *cts,
win_T *wp,
linenr_T lnum,
linenr_T lnum UNUSED,
colnr_T col,
char_u *line,
char_u *ptr)
{
CLEAR_POINTER(cts);
cts->cts_win = wp;
cts->cts_lnum = lnum;
cts->cts_vcol = col;
cts->cts_line = line;
cts->cts_ptr = ptr;
+4 -2
View File
@@ -911,13 +911,15 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
int vim9script = in_vim9script();
int had_comma;
// First check if it's not a curly-braces thing: {expr}.
// First check if it's not a curly-braces expression: {expr}.
// Must do this without evaluating, otherwise a function may be called
// twice. Unfortunately this means we need to call eval1() twice for the
// first item.
// But {} is an empty Dictionary.
// "{}" is an empty Dictionary.
// "#{abc}" is never a curly-braces expression.
if (!vim9script
&& *curly_expr != '}'
&& !literal
&& eval1(&curly_expr, &tv, NULL) == OK
&& *skipwhite(curly_expr) == '}')
return NOTDONE;
+11 -3
View File
@@ -263,9 +263,17 @@ eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv)
if (partial->pt_func != NULL
&& partial->pt_func->uf_def_status != UF_NOT_COMPILED)
{
// FIXME: should create a funccal and link it in current_funccal.
if (call_def_function(partial->pt_func, argc, argv,
partial, NULL, rettv) == FAIL)
funccall_T *fc = create_funccal(partial->pt_func, rettv);
int r;
if (fc == NULL)
return FAIL;
// Shortcut to call a compiled function without overhead.
r = call_def_function(partial->pt_func, argc, argv,
DEF_USE_PT_ARGV, partial, fc, rettv);
remove_funccal();
if (r == FAIL)
return FAIL;
}
else
+46 -9
View File
@@ -1428,10 +1428,12 @@ f_isabsolutepath(typval_T *argvars, typval_T *rettv)
/*
* Create the directory in which "dir" is located, and higher levels when
* needed.
* Set "created" to the full name of the first created directory. It will be
* NULL until that happens.
* Return OK or FAIL.
*/
static int
mkdir_recurse(char_u *dir, int prot)
mkdir_recurse(char_u *dir, int prot, char_u **created)
{
char_u *p;
char_u *updir;
@@ -1449,8 +1451,12 @@ mkdir_recurse(char_u *dir, int prot)
return FAIL;
if (mch_isdir(updir))
r = OK;
else if (mkdir_recurse(updir, prot) == OK)
else if (mkdir_recurse(updir, prot, created) == OK)
{
r = vim_mkdir_emsg(updir, prot);
if (r == OK && created != NULL && *created == NULL)
*created = FullName_save(updir, FALSE);
}
vim_free(updir);
return r;
}
@@ -1464,6 +1470,9 @@ f_mkdir(typval_T *argvars, typval_T *rettv)
char_u *dir;
char_u buf[NUMBUFLEN];
int prot = 0755;
int defer = FALSE;
int defer_recurse = FALSE;
char_u *created = NULL;
rettv->vval.v_number = FAIL;
if (check_restricted() || check_secure())
@@ -1486,13 +1495,21 @@ f_mkdir(typval_T *argvars, typval_T *rettv)
if (argvars[1].v_type != VAR_UNKNOWN)
{
char_u *arg2;
if (argvars[2].v_type != VAR_UNKNOWN)
{
prot = (int)tv_get_number_chk(&argvars[2], NULL);
if (prot == -1)
return;
}
if (STRCMP(tv_get_string(&argvars[1]), "p") == 0)
arg2 = tv_get_string(&argvars[1]);
defer = vim_strchr(arg2, 'D') != NULL;
defer_recurse = vim_strchr(arg2, 'R') != NULL;
if ((defer || defer_recurse) && !can_add_defer())
return;
if (vim_strchr(arg2, 'p') != NULL)
{
if (mch_isdir(dir))
{
@@ -1500,10 +1517,33 @@ f_mkdir(typval_T *argvars, typval_T *rettv)
rettv->vval.v_number = OK;
return;
}
mkdir_recurse(dir, prot);
mkdir_recurse(dir, prot, defer || defer_recurse ? &created : NULL);
}
}
rettv->vval.v_number = vim_mkdir_emsg(dir, prot);
// Handle "D" and "R": deferred deletion of the created directory.
if (rettv->vval.v_number == OK
&& created == NULL && (defer || defer_recurse))
created = FullName_save(dir, FALSE);
if (created != NULL)
{
typval_T tv[2];
tv[0].v_type = VAR_STRING;
tv[0].v_lock = 0;
tv[0].vval.v_string = created;
tv[1].v_type = VAR_STRING;
tv[1].v_lock = 0;
tv[1].vval.v_string = vim_strsave(
(char_u *)(defer_recurse ? "rf" : "d"));
if (tv[0].vval.v_string == NULL || tv[1].vval.v_string == NULL
|| add_defer((char_u *)"delete", 2, tv) == FAIL)
{
vim_free(tv[0].vval.v_string);
vim_free(tv[1].vval.v_string);
}
}
}
/*
@@ -2300,11 +2340,8 @@ f_writefile(typval_T *argvars, typval_T *rettv)
if (fname == NULL)
return;
if (defer && !in_def_function() && get_current_funccal() == NULL)
{
semsg(_(e_str_not_inside_function), "defer");
if (defer && !can_add_defer())
return;
}
// Always open the file in binary mode, library functions have a mind of
// their own about CR-LF conversion.
@@ -2323,7 +2360,7 @@ f_writefile(typval_T *argvars, typval_T *rettv)
tv.v_type = VAR_STRING;
tv.v_lock = 0;
tv.vval.v_string = vim_strsave(fname);
tv.vval.v_string = FullName_save(fname, FALSE);
if (tv.vval.v_string == NULL
|| add_defer((char_u *)"delete", 1, &tv) == FAIL)
{
+1
View File
@@ -8649,6 +8649,7 @@ test_gui_w32_sendevent(dict_T *args)
inputs[0].ki.wVk = vkCode;
if (STRICMP(event, "keyup") == 0)
inputs[0].ki.dwFlags = KEYEVENTF_KEYUP;
(void)SetForegroundWindow(s_hwnd);
SendInput(ARRAYSIZE(inputs), inputs, sizeof(INPUT));
}
else
+3
View File
@@ -21,6 +21,8 @@ int funcdepth_increment(void);
void funcdepth_decrement(void);
int funcdepth_get(void);
void funcdepth_restore(int depth);
funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv);
void remove_funccal(void);
int check_user_func_argcount(ufunc_T *fp, int argcount);
int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict);
void save_funccal(funccal_entry_T *entry);
@@ -58,6 +60,7 @@ void func_ptr_unref(ufunc_T *fp);
void func_ref(char_u *name);
void func_ptr_ref(ufunc_T *fp);
void ex_return(exarg_T *eap);
int can_add_defer(void);
int add_defer(char_u *name, int argcount_arg, typval_T *argvars);
void invoke_all_defer(void);
void ex_call(exarg_T *eap);
+1 -1
View File
@@ -15,7 +15,7 @@ typval_T *lookup_debug_var(char_u *name);
int may_break_in_function(ufunc_T *ufunc);
int exe_typval_instr(typval_T *tv, typval_T *rettv);
char_u *exe_substitute_instr(void);
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, partial_T *partial, funccall_T *funccal, typval_T *rettv);
int call_def_function(ufunc_T *ufunc, int argc_arg, typval_T *argv, int flags, partial_T *partial, funccall_T *funccal, typval_T *rettv);
void unwind_def_callstack(ectx_T *ectx);
void may_invoke_defer_funcs(ectx_T *ectx);
void set_context_in_disassemble_cmd(expand_T *xp, char_u *arg);
+1 -1
View File
@@ -3444,7 +3444,7 @@ regmatch(
linenr_T lnum = rex.reg_firstlnum + rex.lnum;
long_u vcol = 0;
if (lnum > 0 && lnum <= wp->w_buffer->b_ml.ml_line_count)
if (lnum >= 0 && lnum <= wp->w_buffer->b_ml.ml_line_count)
vcol = (long_u)win_linetabsize(wp, lnum, rex.line,
(colnr_T)(rex.input - rex.line));
if (!re_num_cmp(vcol + 1, scan))
+1 -1
View File
@@ -6778,7 +6778,7 @@ nfa_regmatch(
linenr_T lnum = rex.reg_firstlnum + rex.lnum;
long_u vcol = 0;
if (lnum > 0
if (lnum >= 0
&& lnum <= wp->w_buffer->b_ml.ml_line_count)
vcol = (long_u)win_linetabsize(wp, lnum,
rex.line, col);
-1
View File
@@ -4618,7 +4618,6 @@ typedef struct {
// Argument for lbr_chartabsize().
typedef struct {
win_T *cts_win;
linenr_T cts_lnum; // zero when not using text properties
char_u *cts_line; // start of the line
char_u *cts_ptr; // current position in line
#ifdef FEAT_PROP_POPUP
+17 -2
View File
@@ -275,6 +275,21 @@ func Test_assert_fail_fails()
endtry
call assert_match("E1222: String or List required for argument 2", exp)
try
call assert_equal(0, assert_fails('xxx', [#{one: 1}]))
catch
let exp = v:exception
endtry
call assert_match("E731: Using a Dictionary as a String", exp)
let exp = ''
try
call assert_equal(0, assert_fails('xxx', ['E492', #{one: 1}]))
catch
let exp = v:exception
endtry
call assert_match("E731: Using a Dictionary as a String", exp)
try
call assert_equal(1, assert_fails('xxx', 'E492', '', 'burp'))
catch
@@ -289,8 +304,8 @@ func Test_assert_fail_fails()
endtry
call assert_match("E1174: String required for argument 5", exp)
call assert_equal(1, assert_fails('c0', ['', '\1']))
call assert_match("Expected '\\\\\\\\1' but got 'E939: Positive count required: c0': c0", v:errors[0])
call assert_equal(1, assert_fails('c0', ['', '\(.\)\1']))
call assert_match("Expected '\\\\\\\\(.\\\\\\\\)\\\\\\\\1' but got 'E939: Positive count required: c0': c0", v:errors[0])
call remove(v:errors, 0)
endfunc
+5 -10
View File
@@ -28,9 +28,9 @@ endfunc
func Test_set_filename_other_window()
let cwd = getcwd()
call test_autochdir()
call mkdir('Xa')
call mkdir('Xb')
call mkdir('Xc')
call mkdir('Xa', 'R')
call mkdir('Xb', 'R')
call mkdir('Xc', 'R')
try
args Xa/aaa.txt Xb/bbb.txt
set acd
@@ -45,9 +45,6 @@ func Test_set_filename_other_window()
bwipe! aaa.txt
bwipe! bbb.txt
bwipe! ccc.txt
call delete('Xa', 'rf')
call delete('Xb', 'rf')
call delete('Xc', 'rf')
endtry
endfunc
@@ -56,7 +53,7 @@ func Test_acd_win_execute()
set acd
call test_autochdir()
call mkdir('XacdDir')
call mkdir('XacdDir', 'R')
let winid = win_getid()
new XacdDir/file
call assert_match('testdir.XacdDir$', getcwd())
@@ -68,7 +65,6 @@ func Test_acd_win_execute()
bwipe!
set noacd
call chdir(cwd)
call delete('XacdDir', 'rf')
endfunc
func Test_verbose_pwd()
@@ -78,7 +74,7 @@ func Test_verbose_pwd()
edit global.txt
call assert_match('\[global\].*testdir$', execute('verbose pwd'))
call mkdir('Xautodir')
call mkdir('Xautodir', 'R')
split Xautodir/local.txt
lcd Xautodir
call assert_match('\[window\].*testdir[/\\]Xautodir', execute('verbose pwd'))
@@ -112,7 +108,6 @@ func Test_verbose_pwd()
bwipe!
call chdir(cwd)
call delete('Xautodir', 'rf')
endfunc
func Test_multibyte()
+3 -6
View File
@@ -707,14 +707,13 @@ func Test_BufEnter()
call assert_equal('++', g:val)
" Also get BufEnter when editing a directory
call mkdir('Xbufenterdir')
call mkdir('Xbufenterdir', 'D')
split Xbufenterdir
call assert_equal('+++', g:val)
" On MS-Windows we can't edit the directory, make sure we wipe the right
" buffer.
bwipe! Xbufenterdir
call delete('Xbufenterdir', 'd')
au! BufEnter
" Editing a "nofile" buffer doesn't read the file but does trigger BufEnter
@@ -1902,11 +1901,10 @@ func Test_BufWriteCmd()
new
file Xbufwritecmd
set buftype=acwrite
call mkdir('Xbufwritecmd')
call mkdir('Xbufwritecmd', 'D')
write
" BufWriteCmd should be triggered even if a directory has the same name
call assert_equal(1, g:written)
call delete('Xbufwritecmd', 'd')
unlet g:written
au! BufWriteCmd
bwipe!
@@ -2710,7 +2708,7 @@ func Test_throw_in_BufWritePre()
endfunc
func Test_autocmd_in_try_block()
call mkdir('Xintrydir')
call mkdir('Xintrydir', 'R')
au BufEnter * let g:fname = expand('%')
try
edit Xintrydir/
@@ -2719,7 +2717,6 @@ func Test_autocmd_in_try_block()
unlet g:fname
au! BufEnter
call delete('Xintrydir', 'rf')
endfunc
func Test_autocmd_SafeState()
+58
View File
@@ -44,6 +44,64 @@ func Test_mkdir_p()
call assert_fails('call mkdir("abc", [], [])', 'E745:')
endfunc
func DoMkdirDel(name)
call mkdir(a:name, 'pD')
call assert_true(isdirectory(a:name))
endfunc
func DoMkdirDelAddFile(name)
call mkdir(a:name, 'pD')
call assert_true(isdirectory(a:name))
call writefile(['text'], a:name .. '/file')
endfunc
func DoMkdirDelRec(name)
call mkdir(a:name, 'pR')
call assert_true(isdirectory(a:name))
endfunc
func DoMkdirDelRecAddFile(name)
call mkdir(a:name, 'pR')
call assert_true(isdirectory(a:name))
call writefile(['text'], a:name .. '/file')
endfunc
func Test_mkdir_defer_del()
" Xtopdir/tmp is created thus deleted, not Xtopdir itself
call mkdir('Xtopdir', 'R')
call DoMkdirDel('Xtopdir/tmp')
call assert_true(isdirectory('Xtopdir'))
call assert_false(isdirectory('Xtopdir/tmp'))
" Deletion fails because "tmp" contains "sub"
call DoMkdirDel('Xtopdir/tmp/sub')
call assert_true(isdirectory('Xtopdir'))
call assert_true(isdirectory('Xtopdir/tmp'))
call delete('Xtopdir/tmp', 'rf')
" Deletion fails because "tmp" contains "file"
call DoMkdirDelAddFile('Xtopdir/tmp')
call assert_true(isdirectory('Xtopdir'))
call assert_true(isdirectory('Xtopdir/tmp'))
call assert_true(filereadable('Xtopdir/tmp/file'))
call delete('Xtopdir/tmp', 'rf')
" Xtopdir/tmp is created thus deleted, not Xtopdir itself
call DoMkdirDelRec('Xtopdir/tmp')
call assert_true(isdirectory('Xtopdir'))
call assert_false(isdirectory('Xtopdir/tmp'))
" Deletion works even though "tmp" contains "sub"
call DoMkdirDelRec('Xtopdir/tmp/sub')
call assert_true(isdirectory('Xtopdir'))
call assert_false(isdirectory('Xtopdir/tmp'))
" Deletion works even though "tmp" contains "file"
call DoMkdirDelRecAddFile('Xtopdir/tmp')
call assert_true(isdirectory('Xtopdir'))
call assert_false(isdirectory('Xtopdir/tmp'))
endfunc
func Test_line_continuation()
let array = [5,
"\ ignore this
+1 -1
View File
@@ -275,7 +275,7 @@ let s:filename_checks = {
\ 'jam': ['file.jpl', 'file.jpr', 'JAM-file.file', 'JAM.file', 'Prl-file.file', 'Prl.file'],
\ 'java': ['file.java', 'file.jav'],
\ 'javacc': ['file.jj', 'file.jjt'],
\ 'javascript': ['file.js', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'],
\ 'javascript': ['file.js', 'file.jsm', 'file.javascript', 'file.es', 'file.mjs', 'file.cjs'],
\ 'javascript.glimmer': ['file.gjs'],
\ 'javascriptreact': ['file.jsx'],
\ 'jess': ['file.clp'],
+4 -2
View File
@@ -1614,12 +1614,15 @@ func Test_gui_CTRL_SHIFT_V()
endfunc
func Test_gui_dialog_file()
" make sure the file does not exist, otherwise a dialog makes Vim hang
call delete('Xdialfile')
let lines =<< trim END
file Xdialfile
normal axxx
confirm qa
END
call writefile(lines, 'Xlines')
call writefile(lines, 'Xlines', 'D')
let prefix = '!'
if has('win32')
let prefix = '!start '
@@ -1636,7 +1639,6 @@ func Test_gui_dialog_file()
call delete('Xdialog')
call delete('Xdialfile')
call delete('Xlines')
endfunc
" Test for sending low level key presses
+4
View File
@@ -321,6 +321,10 @@ func Test_dict()
" allow key starting with number at the start, not a curly expression
call assert_equal({'1foo': 77}, #{1foo: 77})
" #{expr} is not a curly expression
let x = 'x'
call assert_equal(#{g: x}, #{g:x})
endfunc
" This was allowed in legacy Vim script
+13
View File
@@ -30,11 +30,13 @@ endfunc
func Test_equivalence_re1()
set re=1
call s:equivalence_test()
set re=0
endfunc
func Test_equivalence_re2()
set re=2
call s:equivalence_test()
set re=0
endfunc
func Test_recursive_substitute()
@@ -67,6 +69,7 @@ func Test_eow_with_optional()
let actual = matchlist('abc def', '\(abc\>\)\?\s*\(def\)')
call assert_equal(expected, actual)
endfor
set re=0
endfunc
func Test_backref()
@@ -1141,4 +1144,14 @@ def Test_compare_columns()
prop_type_delete('name')
enddef
def Test_compare_column_matchstr()
enew
set re=1
call assert_equal('aaa', matchstr('aaaaaaaaaaaaaaaaaaaa', '.*\%<5v'))
set re=2
call assert_equal('aaa', matchstr('aaaaaaaaaaaaaaaaaaaa', '.*\%<5v'))
set re=0
enddef
" vim: shiftwidth=2 sts=2 expandtab
+38
View File
@@ -625,6 +625,29 @@ func Test_defer_quitall()
call assert_false(filereadable('XQuitallTwo'))
endfunc
func Test_defer_quitall_in_expr_func()
let lines =<< trim END
def DefIndex(idx: number, val: string): bool
call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
if val == 'b'
qa!
endif
return val == 'c'
enddef
def Test_defer_in_funcref()
assert_equal(2, indexof(['a', 'b', 'c'], funcref('g:DefIndex')))
enddef
call Test_defer_in_funcref()
END
call writefile(lines, 'XdeferQuitallExpr', 'D')
let res = system(GetVimCommandClean() .. ' -X -S XdeferQuitallExpr')
call assert_equal(0, v:shell_error)
call assert_false(filereadable('Xentry0'))
call assert_false(filereadable('Xentry1'))
call assert_false(filereadable('Xentry2'))
endfunc
func FuncIndex(idx, val)
call writefile([a:idx .. ': ' .. a:val], 'Xentry' .. a:idx, 'D')
return a:val == 'c'
@@ -635,6 +658,11 @@ def DefIndex(idx: number, val: string): bool
return val == 'c'
enddef
def DefIndexXtra(xtra: string, idx: number, val: string): bool
call writefile([idx .. ': ' .. val], 'Xentry' .. idx, 'D')
return val == 'c'
enddef
def Test_defer_in_funcref()
assert_equal(2, indexof(['a', 'b', 'c'], function('g:FuncIndex')))
assert_false(filereadable('Xentry0'))
@@ -655,6 +683,16 @@ def Test_defer_in_funcref()
assert_false(filereadable('Xentry0'))
assert_false(filereadable('Xentry1'))
assert_false(filereadable('Xentry2'))
assert_equal(2, indexof(['a', 'b', 'c'], function(g:DefIndexXtra, ['xtra'])))
assert_false(filereadable('Xentry0'))
assert_false(filereadable('Xentry1'))
assert_false(filereadable('Xentry2'))
assert_equal(2, indexof(['a', 'b', 'c'], funcref(g:DefIndexXtra, ['xtra'])))
assert_false(filereadable('Xentry0'))
assert_false(filereadable('Xentry1'))
assert_false(filereadable('Xentry2'))
enddef
+15
View File
@@ -1617,4 +1617,19 @@ func Test_window_alloc_failure()
tabonly
endfunc
func Test_win_equal_last_status()
let save_lines = &lines
set lines=20
set splitbelow
set laststatus=0
split | split | quit
call assert_equal(winheight(1), winheight(2))
let &lines = save_lines
set splitbelow&
set laststatus&
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+13
View File
@@ -950,6 +950,19 @@ func Test_write_with_deferred_delete()
call assert_equal('', glob('XdefdeferDelete'))
endfunc
func DoWriteFile()
call writefile(['text'], 'Xthefile', 'D')
cd ..
endfunc
func Test_write_defer_delete_chdir()
let dir = getcwd()
call DoWriteFile()
call assert_notequal(dir, getcwd())
call chdir(dir)
call assert_equal('', glob('Xthefile'))
endfunc
" Check that buffer is written before triggering QuitPre
func Test_wq_quitpre_autocommand()
edit Xsomefile
+9
View File
@@ -616,6 +616,11 @@ f_assert_fails(typval_T *argvars, typval_T *rettv)
in_assert_fails = TRUE;
do_cmdline_cmd(cmd);
// reset here for any errors reported below
trylevel = save_trylevel;
suppress_errthrow = FALSE;
if (called_emsg == called_emsg_before)
{
prepare_assert_error(&ga);
@@ -654,6 +659,8 @@ f_assert_fails(typval_T *argvars, typval_T *rettv)
CHECK_LIST_MATERIALIZE(list);
tv = &list->lv_first->li_tv;
expected = tv_get_string_buf_chk(tv, buf);
if (expected == NULL)
goto theend;
if (!pattern_match(expected, actual, FALSE))
{
error_found = TRUE;
@@ -667,6 +674,8 @@ f_assert_fails(typval_T *argvars, typval_T *rettv)
{
tv = &list->lv_u.mat.lv_last->li_tv;
expected = tv_get_string_buf_chk(tv, buf);
if (expected == NULL)
goto theend;
if (!pattern_match(expected, actual, FALSE))
{
error_found = TRUE;
+52 -9
View File
@@ -2580,6 +2580,40 @@ funcdepth_restore(int depth)
funcdepth = depth;
}
/*
* Allocate a funccall_T, link it in current_funccal and fill in "fp" and
* "rettv".
* Must be followed by one call to remove_funccal() or cleanup_function_call().
* Returns NULL when allocation fails.
*/
funccall_T *
create_funccal(ufunc_T *fp, typval_T *rettv)
{
funccall_T *fc = ALLOC_CLEAR_ONE(funccall_T);
if (fc == NULL)
return NULL;
fc->fc_caller = current_funccal;
current_funccal = fc;
fc->fc_func = fp;
func_ptr_ref(fp);
fc->fc_rettv = rettv;
return fc;
}
/*
* To be called when returning from a compiled function; restores
* current_funccal.
*/
void
remove_funccal()
{
funccall_T *fc = current_funccal;
current_funccal = fc->fc_caller;
free_funccal(fc);
}
/*
* Call a user function.
*/
@@ -2627,20 +2661,15 @@ call_user_func(
line_breakcheck(); // check for CTRL-C hit
fc = ALLOC_CLEAR_ONE(funccall_T);
fc = create_funccal(fp, rettv);
if (fc == NULL)
return;
fc->fc_caller = current_funccal;
current_funccal = fc;
fc->fc_func = fp;
fc->fc_rettv = rettv;
fc->fc_level = ex_nesting_level;
// Check if this function has a breakpoint.
fc->fc_breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
fc->fc_dbg_tick = debug_tick;
// Set up fields for closure.
ga_init2(&fc->fc_ufuncs, sizeof(ufunc_T *), 1);
func_ptr_ref(fp);
if (fp->uf_def_status != UF_NOT_COMPILED)
{
@@ -2653,7 +2682,7 @@ call_user_func(
profile_may_start_func(&profile_info, fp, caller);
#endif
sticky_cmdmod_flags = 0;
call_def_function(fp, argcount, argvars, funcexe->fe_partial,
call_def_function(fp, argcount, argvars, 0, funcexe->fe_partial,
fc, rettv);
funcdepth_decrement();
#ifdef FEAT_PROFILE
@@ -2661,8 +2690,7 @@ call_user_func(
|| (caller != NULL && caller->uf_profiling)))
profile_may_end_func(&profile_info, fp, caller);
#endif
current_funccal = fc->fc_caller;
free_funccal(fc);
remove_funccal();
sticky_cmdmod_flags = save_sticky_cmdmod_flags;
return;
}
@@ -5621,6 +5649,21 @@ ex_defer_inner(
return add_defer(name, argcount, argvars);
}
/*
* Return TRUE if currently inside a function call.
* Give an error message and return FALSE when not.
*/
int
can_add_defer(void)
{
if (!in_def_function() && get_current_funccal() == NULL)
{
semsg(_(e_str_not_inside_function), "defer");
return FALSE;
}
return TRUE;
}
/*
* Add a deferred call for "name" with arguments "argvars[argcount]".
* Consumes "argvars[]".
+26
View File
@@ -718,6 +718,32 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
412,
/**/
411,
/**/
410,
/**/
409,
/**/
408,
/**/
407,
/**/
406,
/**/
405,
/**/
404,
/**/
403,
/**/
402,
/**/
401,
/**/
400,
/**/
399,
/**/
+2
View File
@@ -759,3 +759,5 @@ typedef enum {
#define TVTT_DO_MEMBER 1
#define TVTT_MORE_SPECIFIC 2 // get most specific type for member
// flags for call_def_function()
#define DEF_USE_PT_ARGV 1 // use the partial arguments
+21 -12
View File
@@ -5272,16 +5272,21 @@ call_def_function(
ufunc_T *ufunc,
int argc_arg, // nr of arguments
typval_T *argv, // arguments
int flags, // DEF_ flags
partial_T *partial, // optional partial for context
funccall_T *funccal,
typval_T *rettv) // return value
{
ectx_T ectx; // execution context
int argc = argc_arg;
int partial_argc = partial == NULL
|| (flags & DEF_USE_PT_ARGV) == 0
? 0 : partial->pt_argc;
int total_argc = argc + partial_argc;
typval_T *tv;
int idx;
int ret = FAIL;
int defcount = ufunc->uf_args.ga_len - argc;
int defcount = ufunc->uf_args.ga_len - total_argc;
sctx_T save_current_sctx = current_sctx;
int did_emsg_before = did_emsg_cumul + did_emsg;
int save_suppress_errthrow = suppress_errthrow;
@@ -5345,14 +5350,14 @@ call_def_function(
ectx.ec_did_emsg_before = did_emsg_before;
++ex_nesting_level;
idx = argc - ufunc->uf_args.ga_len;
idx = total_argc - ufunc->uf_args.ga_len;
if (idx > 0 && ufunc->uf_va_name == NULL)
{
semsg(NGETTEXT(e_one_argument_too_many, e_nr_arguments_too_many,
idx), idx);
idx), idx);
goto failed_early;
}
idx = argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len;
idx = total_argc - ufunc->uf_args.ga_len + ufunc->uf_def_args.ga_len;
if (idx < 0)
{
semsg(NGETTEXT(e_one_argument_too_few, e_nr_arguments_too_few,
@@ -5360,15 +5365,19 @@ call_def_function(
goto failed_early;
}
// Put arguments on the stack, but no more than what the function expects.
// A lambda can be called with more arguments than it uses.
for (idx = 0; idx < argc
// Put values from the partial and arguments on the stack, but no more than
// what the function expects. A lambda can be called with more arguments
// than it uses.
for (idx = 0; idx < total_argc
&& (ufunc->uf_va_name != NULL || idx < ufunc->uf_args.ga_len);
++idx)
{
int argv_idx = idx - partial_argc;
tv = idx < partial_argc ? partial->pt_argv + idx : argv + argv_idx;
if (idx >= ufunc->uf_args.ga_len - ufunc->uf_def_args.ga_len
&& argv[idx].v_type == VAR_SPECIAL
&& argv[idx].vval.v_number == VVAL_NONE)
&& tv->v_type == VAR_SPECIAL
&& tv->vval.v_number == VVAL_NONE)
{
// Use the default value.
STACK_TV_BOT(0)->v_type = VAR_UNKNOWN;
@@ -5377,10 +5386,10 @@ call_def_function(
{
if (ufunc->uf_arg_types != NULL && idx < ufunc->uf_args.ga_len
&& check_typval_arg_type(
ufunc->uf_arg_types[idx], &argv[idx],
NULL, idx + 1) == FAIL)
ufunc->uf_arg_types[idx], tv,
NULL, argv_idx + 1) == FAIL)
goto failed_early;
copy_tv(&argv[idx], STACK_TV_BOT(0));
copy_tv(tv, STACK_TV_BOT(0));
}
++ectx.ec_stack.ga_len;
}
+8 -6
View File
@@ -2718,6 +2718,14 @@ win_close(win_T *win, int free_buf)
// using the window.
check_cursor();
}
/*
* If last window has a status line now and we don't want one, remove the
* status line. Do this before win_equal(), because it may change the
* height of a window
*/
last_status(FALSE);
if (p_ea && (*p_ead == 'b' || *p_ead == dir))
// If the frame of the closed window contains the new current window,
// only resize that frame. Otherwise resize all windows.
@@ -2747,12 +2755,6 @@ win_close(win_T *win, int free_buf)
--dont_parse_messages;
#endif
/*
* If last window has a status line now and we don't want one,
* remove the status line.
*/
last_status(FALSE);
// After closing the help window, try restoring the window layout from
// before it was opened.
if (help_window)