Compare commits

...

16 Commits

Author SHA1 Message Date
Bram Moolenaar
ccc25aa285 patch 8.2.2660: Vim9: no error for declaration with trailing text
Problem:    Vim9: no error for declaration with trailing text.
Solution:   Give an error. (closes #8014)
2021-03-26 21:27:52 +01:00
Bram Moolenaar
c61cb8bfe1 patch 8.2.2659: eval test fails because for loop on string works
Problem:    Eval test fails because for loop on string works.
Solution:   Check looping over function reference fails.
2021-03-26 20:56:45 +01:00
Bram Moolenaar
74e54fcb44 patch 8.2.2658: :for cannot loop over a string
Problem:    :for cannot loop over a string.
Solution:   Accept a string argument and iterate over its characters.
2021-03-26 20:41:29 +01:00
Bram Moolenaar
522eefd9a2 patch 8.2.2657: Vim9: error message for declaring variable in for loop
Problem:    Vim9: error message for declaring variable in for loop.
Solution:   Clear variables when entering block again. (closes #8012)
2021-03-26 18:49:22 +01:00
Bram Moolenaar
a2b3e7dc92 patch 8.2.2656: some command line arguments and regexp errors not tested
Problem:    Some command line arguments and regexp errors not tested.
Solution:   Add a few test cases. (Dominique Pellé, closes #8013)
2021-03-26 17:24:34 +01:00
Bram Moolenaar
0a1a6a1aa4 patch 8.2.2655: The -w command line argument doesn't work
Problem:    The -w command line argument doesn't work.
Solution:   Don't set 'window' when set with the -w argument. (closes #8011)
2021-03-26 14:14:18 +01:00
Bram Moolenaar
ff87140046 patch 8.2.2654: Vim9: getting a character from a string can be slow
Problem:    Vim9: getting a character from a string can be slow.
Solution:   Avoid a function call to get the character byte size. (#8000)
2021-03-26 13:34:05 +01:00
Bram Moolenaar
3a0f092ac0 patch 8.2.2653: build failure
Problem:    Build failure.
Solution:   Add missing changes.
2021-03-25 22:22:30 +01:00
Bram Moolenaar
fa984418e7 patch 8.2.2652: Vim9: can use command modifier without an effect
Problem:    Vim9: can use command modifier without an effect.
Solution:   Give an error for a misplaced command modifier.  Fix error message
            number.
2021-03-25 22:15:28 +01:00
Bram Moolenaar
a91a71322d patch 8.2.2651: Vim9: restoring command modifiers happens after jump
Problem:    Vim9: restoring command modifiers happens after jump.
Solution:   Move the restore instruction to before the jump. (closes #8006)
            Also handle for and while.
2021-03-25 21:12:15 +01:00
Bram Moolenaar
2fecb53115 patch 8.2.2650: Vim9: command modifiers not handled in nested function
Problem:    Vim9: command modifiers not handled in nested function.
Solution:   Keep function-local info in a structure and save it on the stack.
2021-03-24 22:00:56 +01:00
Bram Moolenaar
1ff89deeaa patch 8.2.2649: Vim9: some wincmd arguments cause a white space error
Problem:    Vim9: some wincmd arguments cause a white space error.
Solution:   Insert a space before the count. (closes #8001)
2021-03-24 20:08:12 +01:00
Bram Moolenaar
c54f347d63 patch 8.2.2648: terminal resize test sometimes hangs
Problem:    Terminal resize test sometimes hangs.
Solution:   Wait for the shell to display a prompt and other output.
2021-03-23 19:22:12 +01:00
Bram Moolenaar
f4a2ed0714 patch 8.2.2647: terminal test sometimes hangs
Problem:    Terminal test sometimes hangs.
Solution:   Wait for the shell to display a prompt.
2021-03-23 16:25:09 +01:00
Bram Moolenaar
f28f2ac425 patch 8.2.2646: Vim9: error for not using string doesn't mentionargument
Problem:    Vim9: error for not using string doesn't mention argument.
Solution:   Add argument number.
2021-03-22 22:21:26 +01:00
Bram Moolenaar
49f1e9ec3e patch 8.2.2645: using inline function is not properly tested
Problem:    Using inline function is not properly tested.
Solution:   Add test cases, esp. for errors.  Minor code improvements.
2021-03-22 20:49:02 +01:00
31 changed files with 881 additions and 195 deletions

View File

@@ -439,8 +439,8 @@ Changing the order of items in a list: >
For loop ~
The |:for| loop executes commands for each item in a list. A variable is set
to each item in the list in sequence. Example: >
The |:for| loop executes commands for each item in a List, String or Blob.
A variable is set to each item in sequence. Example with a List: >
:for item in mylist
: call Doit(item)
:endfor
@@ -457,7 +457,7 @@ If all you want to do is modify each item in the list then the |map()|
function will be a simpler method than a for loop.
Just like the |:let| command, |:for| also accepts a list of variables. This
requires the argument to be a list of lists. >
requires the argument to be a List of Lists. >
:for [lnum, col] in [[1, 3], [2, 8], [3, 0]]
: call Doit(lnum, col)
:endfor
@@ -473,6 +473,14 @@ It is also possible to put remaining items in a List variable: >
: endif
:endfor
For a Blob one byte at a time is used.
For a String one character, including any composing characters, is used as a
String. Example: >
for c in text
echo 'This character is ' .. c
endfor
List functions ~
*E714*

View File

@@ -381,3 +381,13 @@ EXTERN char e_missing_end_block[]
INIT(= N_("E1171: Missing } after inline function"));
EXTERN char e_cannot_use_default_values_in_lambda[]
INIT(= N_("E1172: Cannot use default values in a lambda"));
EXTERN char e_text_found_after_enddef_str[]
INIT(= N_("E1173: Text found after enddef: %s"));
EXTERN char e_string_required_for_argument_nr[]
INIT(= N_("E1174: String required for argument %d"));
EXTERN char e_non_empty_string_required_for_argument_nr[]
INIT(= N_("E1175: Non-empty string required for argument %d"));
EXTERN char e_misplaced_command_modifier[]
INIT(= N_("E1176: Misplaced command modifier"));
EXTERN char e_for_loop_on_str_not_supported[]
INIT(= N_("E1177: For loop on %s not supported"));

View File

@@ -41,6 +41,8 @@ typedef struct
list_T *fi_list; // list being used
int fi_bi; // index of blob
blob_T *fi_blob; // blob being used
char_u *fi_string; // copy of string being used
int fi_byte_idx; // byte index in fi_string
} forinfo_T;
static int tv_op(typval_T *tv1, typval_T *tv2, char_u *op);
@@ -1738,6 +1740,14 @@ eval_for_line(
}
clear_tv(&tv);
}
else if (tv.v_type == VAR_STRING)
{
fi->fi_byte_idx = 0;
fi->fi_string = tv.vval.v_string;
tv.vval.v_string = NULL;
if (fi->fi_string == NULL)
fi->fi_string = vim_strsave((char_u *)"");
}
else
{
emsg(_(e_listreq));
@@ -1790,7 +1800,23 @@ next_for_item(void *fi_void, char_u *arg)
tv.vval.v_number = blob_get(fi->fi_blob, fi->fi_bi);
++fi->fi_bi;
return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, flag, NULL) == OK;
fi->fi_varcount, flag, NULL) == OK;
}
if (fi->fi_string != NULL)
{
typval_T tv;
int len;
len = mb_ptr2len(fi->fi_string + fi->fi_byte_idx);
if (len == 0)
return FALSE;
tv.v_type = VAR_STRING;
tv.v_lock = VAR_FIXED;
tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len);
fi->fi_byte_idx += len;
return ex_let_vars(arg, &tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, flag, NULL) == OK;
}
item = fi->fi_lw.lw_item;
@@ -1800,7 +1826,7 @@ next_for_item(void *fi_void, char_u *arg)
{
fi->fi_lw.lw_item = item->li_next;
result = (ex_let_vars(arg, &item->li_tv, TRUE, fi->fi_semicolon,
fi->fi_varcount, flag, NULL) == OK);
fi->fi_varcount, flag, NULL) == OK);
}
return result;
}
@@ -1813,13 +1839,17 @@ free_for_info(void *fi_void)
{
forinfo_T *fi = (forinfo_T *)fi_void;
if (fi != NULL && fi->fi_list != NULL)
if (fi == NULL)
return;
if (fi->fi_list != NULL)
{
list_rem_watch(fi->fi_list, &fi->fi_lw);
list_unref(fi->fi_list);
}
if (fi != NULL && fi->fi_blob != NULL)
else if (fi->fi_blob != NULL)
blob_unref(fi->fi_blob);
else
vim_free(fi->fi_string);
vim_free(fi);
}

View File

@@ -789,8 +789,11 @@ ex_let(exarg_T *eap)
{
if (vim9script)
{
// Vim9 declaration ":var name: type"
arg = vim9_declare_scriptvar(eap, arg);
if (!ends_excmd2(eap->cmd, skipwhite(argend)))
semsg(_(e_trailing_arg), argend);
else
// Vim9 declaration ":var name: type"
arg = vim9_declare_scriptvar(eap, arg);
}
else
{

View File

@@ -2969,6 +2969,33 @@ parse_command_modifiers(
return OK;
}
/*
* Return TRUE if "cmod" has anything set.
*/
int
has_cmdmod(cmdmod_T *cmod)
{
return cmod->cmod_flags != 0
|| cmod->cmod_split != 0
|| cmod->cmod_verbose != 0
|| cmod->cmod_tab != 0
|| cmod->cmod_filter_regmatch.regprog != NULL;
}
/*
* If Vim9 script and "cmdmod" has anything set give an error and return TRUE.
*/
int
cmdmod_error(void)
{
if (in_vim9script() && has_cmdmod(&cmdmod))
{
emsg(_(e_misplaced_command_modifier));
return TRUE;
}
return FALSE;
}
/*
* Apply the command modifiers. Saves current state in "cmdmod", call
* undo_cmdmod() later.

View File

@@ -1011,6 +1011,8 @@ ex_endif(exarg_T *eap)
{
cstack_T *cstack = eap->cstack;
if (cmdmod_error())
return;
did_endif = TRUE;
if (cstack->cs_idx < 0
|| (cstack->cs_flags[cstack->cs_idx]
@@ -1152,6 +1154,32 @@ ex_while(exarg_T *eap)
++cstack->cs_looplevel;
cstack->cs_line[cstack->cs_idx] = -1;
}
else
{
if (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid))
{
scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
int i;
// Any variables defined in the previous round are no longer
// visible.
for (i = cstack->cs_script_var_len[cstack->cs_idx];
i < si->sn_var_vals.ga_len; ++i)
{
svar_T *sv = ((svar_T *)si->sn_var_vals.ga_data) + i;
// sv_name is set to NULL if it was already removed. This
// happens when it was defined in an inner block and no
// functions were defined there.
if (sv->sv_name != NULL)
// Remove a variable declared inside the block, if it
// still exists, from sn_vars.
hide_script_var(si, i, FALSE);
}
cstack->cs_script_var_len[cstack->cs_idx] =
si->sn_var_vals.ga_len;
}
}
cstack->cs_flags[cstack->cs_idx] =
eap->cmdidx == CMD_while ? CSF_WHILE : CSF_FOR;
@@ -1173,6 +1201,9 @@ ex_while(exarg_T *eap)
void *fi;
evalarg_T evalarg;
/*
* ":for var in list-expr"
*/
CLEAR_FIELD(evalarg);
evalarg.eval_flags = skip ? 0 : EVAL_EVALUATE;
if (getline_equal(eap->getline, eap->cookie, getsourceline))
@@ -1181,9 +1212,6 @@ ex_while(exarg_T *eap)
evalarg.eval_cookie = eap->cookie;
}
/*
* ":for var in list-expr"
*/
if ((cstack->cs_lflags & CSL_HAD_LOOP) != 0)
{
// Jumping here from a ":continue" or ":endfor": use the
@@ -1314,6 +1342,9 @@ ex_endwhile(exarg_T *eap)
int csf;
int fl;
if (cmdmod_error())
return;
if (eap->cmdidx == CMD_endwhile)
{
err = e_while;
@@ -1379,10 +1410,8 @@ ex_endwhile(exarg_T *eap)
&& dbg_check_skipped(eap))
(void)do_intthrow(cstack);
/*
* Set loop flag, so do_cmdline() will jump back to the matching
* ":while" or ":for".
*/
// Set loop flag, so do_cmdline() will jump back to the matching
// ":while" or ":for".
cstack->cs_lflags |= CSL_HAD_ENDLOOP;
}
}
@@ -1539,6 +1568,9 @@ ex_try(exarg_T *eap)
int skip;
cstack_T *cstack = eap->cstack;
if (cmdmod_error())
return;
if (cstack->cs_idx == CSTACK_LEN - 1)
eap->errmsg = _("E601: :try nesting too deep");
else
@@ -1617,6 +1649,9 @@ ex_catch(exarg_T *eap)
cstack_T *cstack = eap->cstack;
char_u *pat;
if (cmdmod_error())
return;
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
{
eap->errmsg = _(e_catch);
@@ -1777,6 +1812,9 @@ ex_finally(exarg_T *eap)
int pending = CSTP_NONE;
cstack_T *cstack = eap->cstack;
if (cmdmod_error())
return;
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
eap->errmsg = _(e_finally);
else
@@ -1906,6 +1944,9 @@ ex_endtry(exarg_T *eap)
void *rettv = NULL;
cstack_T *cstack = eap->cstack;
if (cmdmod_error())
return;
if (cstack->cs_trylevel <= 0 || cstack->cs_idx < 0)
eap->errmsg = _(e_no_endtry);
else

View File

@@ -861,7 +861,7 @@ f_delete(typval_T *argvars, typval_T *rettv)
void
f_executable(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
// Check in $PATH and also check directly if there is a directory name.
@@ -876,7 +876,7 @@ f_exepath(typval_T *argvars, typval_T *rettv)
{
char_u *p = NULL;
if (in_vim9script() && check_for_nonempty_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_nonempty_string(&argvars[0], 1) == FAIL)
return;
(void)mch_can_exe(tv_get_string(&argvars[0]), &p, TRUE);
rettv->v_type = VAR_STRING;
@@ -893,7 +893,7 @@ f_filereadable(typval_T *argvars, typval_T *rettv)
char_u *p;
int n;
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
#ifndef O_NONBLOCK
# define O_NONBLOCK 0
@@ -918,7 +918,7 @@ f_filereadable(typval_T *argvars, typval_T *rettv)
void
f_filewritable(typval_T *argvars, typval_T *rettv)
{
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
rettv->vval.v_number = filewritable(tv_get_string(&argvars[0]));
}
@@ -942,7 +942,7 @@ findfilendir(
rettv->vval.v_string = NULL;
rettv->v_type = VAR_STRING;
if (in_vim9script() && check_for_nonempty_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_nonempty_string(&argvars[0], 1) == FAIL)
return;
#ifdef FEAT_SEARCHPATH
@@ -1023,8 +1023,8 @@ f_fnamemodify(typval_T *argvars, typval_T *rettv)
char_u *fbuf = NULL;
char_u buf[NUMBUFLEN];
if (in_vim9script() && (check_for_string(&argvars[0]) == FAIL
|| check_for_string(&argvars[1]) == FAIL))
if (in_vim9script() && (check_for_string(&argvars[0], 1) == FAIL
|| check_for_string(&argvars[1], 2) == FAIL))
return;
fname = tv_get_string_chk(&argvars[0]);
mods = tv_get_string_buf_chk(&argvars[1], buf);
@@ -1135,7 +1135,7 @@ f_getfperm(typval_T *argvars, typval_T *rettv)
char_u *perm = NULL;
char_u permbuf[] = "---------";
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
fname = tv_get_string(&argvars[0]);
@@ -1154,7 +1154,7 @@ f_getfsize(typval_T *argvars, typval_T *rettv)
char_u *fname;
stat_T st;
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
fname = tv_get_string(&argvars[0]);
@@ -1184,7 +1184,7 @@ f_getftime(typval_T *argvars, typval_T *rettv)
char_u *fname;
stat_T st;
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
fname = tv_get_string(&argvars[0]);
if (mch_stat((char *)fname, &st) >= 0)
@@ -1230,7 +1230,7 @@ f_getftype(typval_T *argvars, typval_T *rettv)
stat_T st;
char_u *type = NULL;
if (in_vim9script() && check_for_string(&argvars[0]) == FAIL)
if (in_vim9script() && check_for_string(&argvars[0], 1) == FAIL)
return;
fname = tv_get_string(&argvars[0]);
@@ -2410,6 +2410,11 @@ f_browse(typval_T *argvars UNUSED, typval_T *rettv)
char_u buf2[NUMBUFLEN];
int error = FALSE;
if (in_vim9script()
&& (check_for_string(&argvars[1], 2) == FAIL
|| check_for_string(&argvars[2], 3) == FAIL
|| check_for_string(&argvars[3], 4) == FAIL))
return;
save = (int)tv_get_number_chk(&argvars[0], &error);
title = tv_get_string_chk(&argvars[1]);
initdir = tv_get_string_buf_chk(&argvars[2], buf);

View File

@@ -5551,7 +5551,7 @@ f_setcellwidths(typval_T *argvars, typval_T *rettv UNUSED)
void
f_charclass(typval_T *argvars, typval_T *rettv UNUSED)
{
if (check_for_string(&argvars[0]) == FAIL)
if (check_for_string(&argvars[0], 1) == FAIL)
return;
rettv->vval.v_number = mb_get_class(argvars[0].vval.v_string);
}

View File

@@ -8,6 +8,8 @@ void *getline_cookie(char_u *(*fgetline)(int, void *, int, getline_opt_T), void
char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
char *ex_errmsg(char *msg, char_u *arg);
int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, int skip_only);
int has_cmdmod(cmdmod_T *cmod);
int cmdmod_error(void);
void apply_cmdmod(cmdmod_T *cmod);
void undo_cmdmod(cmdmod_T *cmod);
int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);

View File

@@ -9,8 +9,8 @@ varnumber_T tv_get_number_chk(typval_T *varp, int *denote);
varnumber_T tv_get_bool(typval_T *varp);
varnumber_T tv_get_bool_chk(typval_T *varp, int *denote);
float_T tv_get_float(typval_T *varp);
int check_for_string(typval_T *tv);
int check_for_nonempty_string(typval_T *tv);
int check_for_string(typval_T *tv, int arg);
int check_for_nonempty_string(typval_T *tv, int arg);
char_u *tv_get_string(typval_T *varp);
char_u *tv_get_string_strict(typval_T *varp);
char_u *tv_get_string_buf(typval_T *varp, char_u *buf);

View File

@@ -3365,8 +3365,9 @@ win_new_shellsize(void)
ui_new_shellsize();
if (old_Rows != Rows)
{
// if 'window' uses the whole screen, keep it using that
if (p_window == old_Rows - 1 || old_Rows == 0)
// If 'window' uses the whole screen, keep it using that.
// Don't change it when set with "-w size" on the command line.
if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0))
p_window = Rows - 1;
old_Rows = Rows;
shell_new_rows(); // update window sizes

View File

@@ -66,7 +66,7 @@ endfunc
func Test_for_invalid()
call assert_fails("for x in 99", 'E714:')
call assert_fails("for x in 'asdf'", 'E714:')
call assert_fails("for x in function('winnr')", 'E714:')
call assert_fails("for x in {'a': 9}", 'E714:')
if 0

View File

@@ -924,8 +924,17 @@ func Test_regexp_error()
call assert_fails("call matchlist('x x', '\\%#=1 \\ze*')", 'E888:')
call assert_fails("call matchlist('x x', '\\%#=2 \\zs*')", 'E888:')
call assert_fails("call matchlist('x x', '\\%#=2 \\ze*')", 'E888:')
call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
call assert_fails("call matchstr('abcd', '\\%o841\\%o142')", 'E678:')
call assert_fails("call matchstr('abcd', '\\%#=2\\%2147483647c')", 'E951:')
call assert_fails("call matchstr('abcd', '\\%#=2\\%2147483647l')", 'E951:')
call assert_fails("call matchstr('abcd', '\\%#=2\\%2147483647v')", 'E951:')
call assert_fails('exe "normal /\\%#=1\\%[x\\%[x]]\<CR>"', 'E369:')
call assert_fails('exe "normal /\\%#=2\\%2147483647l\<CR>"', 'E951:')
call assert_fails('exe "normal /\\%#=2\\%2147483647c\<CR>"', 'E951:')
call assert_fails('exe "normal /\\%#=2\\%102261126v\<CR>"', 'E951:')
call assert_fails('exe "normal /\\%#=2\\%2147483646l\<CR>"', 'E486:')
call assert_fails('exe "normal /\\%#=2\\%2147483646c\<CR>"', 'E486:')
call assert_fails('exe "normal /\\%#=2\\%102261125v\<CR>"', 'E486:')
call assert_equal('', matchstr('abcd', '\%o181\%o142'))
endfunc

View File

@@ -860,10 +860,12 @@ func Test_t_arg()
\ 'Xtags')
call writefile([' first', ' second', ' third'], 'Xfile1')
if RunVim(before, after, '-t second')
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'))
call delete('Xtestout')
endif
for t_arg in ['-t second', '-tsecond']
if RunVim(before, after, '-t second')
call assert_equal(['Xfile1:L2C5'], readfile('Xtestout'), t_arg)
call delete('Xtestout')
endif
endfor
call delete('Xtags')
call delete('Xfile1')
@@ -1045,6 +1047,7 @@ endfunc
func Test_w_arg()
" Can't catch the output of gvim.
CheckNotGui
call writefile(["iVim Editor\<Esc>:q!\<CR>"], 'Xscriptin', 'b')
if RunVim([], [], '-s Xscriptin -w Xscriptout')
call assert_equal(["iVim Editor\e:q!\r"], readfile('Xscriptout'))
@@ -1060,6 +1063,16 @@ func Test_w_arg()
call assert_equal("Cannot open for script output: \"Xdir\"\n", m)
call delete("Xdir", 'rf')
endif
" A number argument sets the 'window' option
call writefile(["iwindow \<C-R>=&window\<CR>\<Esc>:wq! Xresult\<CR>"], 'Xscriptin', 'b')
for w_arg in ['-w 17', '-w17']
if RunVim([], [], '-s Xscriptin ' .. w_arg)
call assert_equal(["window 17"], readfile('Xresult'), w_arg)
call delete('Xresult')
endif
endfor
call delete('Xscriptin')
endfunc
" Test for the "-s scriptin" argument

View File

@@ -889,6 +889,8 @@ endfunc
func TerminalTmap(remap)
let buf = Run_shell_in_terminal({})
" Wait for the shell to display a prompt
call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
call assert_equal('t', mode())
if a:remap

View File

@@ -246,6 +246,10 @@ func Test_terminal_resize()
set statusline=x
terminal
call assert_equal(2, winnr('$'))
let buf = bufnr()
" Wait for the shell to display a prompt
call WaitForAssert({-> assert_notequal('', term_getline(buf, 1))})
" Fill the terminal with text.
if has('win32')
@@ -253,6 +257,9 @@ func Test_terminal_resize()
else
call feedkeys("ls\<CR>", 'xt')
endif
" Wait for some output
call WaitForAssert({-> assert_notequal('', term_getline(buf, 3))})
" Go to Terminal-Normal mode for a moment.
call feedkeys("\<C-W>N", 'xt')
" Open a new window
@@ -263,6 +270,7 @@ func Test_terminal_resize()
close
call assert_equal(2, winnr('$'))
call feedkeys("exit\<CR>", 'xt')
call TermWait(buf)
set statusline&
endfunc

View File

@@ -1290,6 +1290,8 @@ def Test_var_declaration()
other = 1234
g:other_var = other
var xyz: string # comment
# type is inferred
var s:dict = {['a']: 222}
def GetDictVal(key: any)
@@ -1365,7 +1367,7 @@ def Test_var_declaration_fails()
vim9script
var 9var: string
END
CheckScriptFailure(lines, 'E475:')
CheckScriptFailure(lines, 'E488:')
CheckDefFailure(['var foo.bar = 2'], 'E1087:')
CheckDefFailure(['var foo[3] = 2'], 'E1087:')
@@ -1617,6 +1619,11 @@ def Test_expr_error_no_assign()
echo x
END
CheckScriptFailureList(lines, ['E1154:', 'E121:'])
lines =<< trim END
var x: string 'string'
END
CheckDefAndScriptFailure(lines, 'E488:')
enddef

View File

@@ -125,6 +125,23 @@ def Test_append()
assert_equal(['0', 'one', '1', 'two', '2'], getline(1, 6))
enddef
def Test_browse()
CheckFeature browse
var lines =<< trim END
call browse(1, 2, 3, 4)
END
CheckDefExecAndScriptFailure(lines, 'E1174: String required for argument 2')
lines =<< trim END
call browse(1, 'title', 3, 4)
END
CheckDefExecAndScriptFailure(lines, 'E1174: String required for argument 3')
lines =<< trim END
call browse(1, 'title', 'dir', 4)
END
CheckDefExecAndScriptFailure(lines, 'E1174: String required for argument 4')
enddef
def Test_buflisted()
var res: bool = buflisted('asdf')
assert_equal(false, res)
@@ -223,14 +240,14 @@ def Test_executable()
assert_false(executable(""))
assert_false(executable(test_null_string()))
CheckDefExecFailure(['echo executable(123)'], 'E928:')
CheckDefExecFailure(['echo executable(true)'], 'E928:')
CheckDefExecFailure(['echo executable(123)'], 'E1174:')
CheckDefExecFailure(['echo executable(true)'], 'E1174:')
enddef
def Test_exepath()
CheckDefExecFailure(['echo exepath(true)'], 'E928:')
CheckDefExecFailure(['echo exepath(v:null)'], 'E928:')
CheckDefExecFailure(['echo exepath("")'], 'E1142:')
CheckDefExecFailure(['echo exepath(true)'], 'E1174:')
CheckDefExecFailure(['echo exepath(v:null)'], 'E1174:')
CheckDefExecFailure(['echo exepath("")'], 'E1175:')
enddef
def Test_expand()
@@ -374,28 +391,28 @@ def Test_filereadable()
assert_false(filereadable(""))
assert_false(filereadable(test_null_string()))
CheckDefExecFailure(['echo filereadable(123)'], 'E928:')
CheckDefExecFailure(['echo filereadable(true)'], 'E928:')
CheckDefExecFailure(['echo filereadable(123)'], 'E1174:')
CheckDefExecFailure(['echo filereadable(true)'], 'E1174:')
enddef
def Test_filewritable()
assert_false(filewritable(""))
assert_false(filewritable(test_null_string()))
CheckDefExecFailure(['echo filewritable(123)'], 'E928:')
CheckDefExecFailure(['echo filewritable(true)'], 'E928:')
CheckDefExecFailure(['echo filewritable(123)'], 'E1174:')
CheckDefExecFailure(['echo filewritable(true)'], 'E1174:')
enddef
def Test_finddir()
CheckDefExecFailure(['echo finddir(true)'], 'E928:')
CheckDefExecFailure(['echo finddir(v:null)'], 'E928:')
CheckDefExecFailure(['echo finddir("")'], 'E1142:')
CheckDefExecFailure(['echo finddir(true)'], 'E1174:')
CheckDefExecFailure(['echo finddir(v:null)'], 'E1174:')
CheckDefExecFailure(['echo finddir("")'], 'E1175:')
enddef
def Test_findfile()
CheckDefExecFailure(['echo findfile(true)'], 'E928:')
CheckDefExecFailure(['echo findfile(v:null)'], 'E928:')
CheckDefExecFailure(['echo findfile("")'], 'E1142:')
CheckDefExecFailure(['echo findfile(true)'], 'E1174:')
CheckDefExecFailure(['echo findfile(v:null)'], 'E1174:')
CheckDefExecFailure(['echo findfile("")'], 'E1175:')
enddef
def Test_flattennew()
@@ -421,9 +438,9 @@ def Test_fnamemodify()
CheckDefSuccess(['echo fnamemodify("file", test_null_string())'])
CheckDefSuccess(['echo fnamemodify("file", "")'])
CheckDefExecFailure(['echo fnamemodify(true, ":p")'], 'E928:')
CheckDefExecFailure(['echo fnamemodify(v:null, ":p")'], 'E928:')
CheckDefExecFailure(['echo fnamemodify("file", true)'], 'E928:')
CheckDefExecFailure(['echo fnamemodify(true, ":p")'], 'E1174: String required for argument 1')
CheckDefExecFailure(['echo fnamemodify(v:null, ":p")'], 'E1174: String required for argument 1')
CheckDefExecFailure(['echo fnamemodify("file", true)'], 'E1174: String required for argument 2')
enddef
def Wrong_dict_key_type(items: list<number>): list<number>
@@ -524,32 +541,32 @@ def Test_getfperm()
assert_equal('', getfperm(""))
assert_equal('', getfperm(test_null_string()))
CheckDefExecFailure(['echo getfperm(true)'], 'E928:')
CheckDefExecFailure(['echo getfperm(v:null)'], 'E928:')
CheckDefExecFailure(['echo getfperm(true)'], 'E1174:')
CheckDefExecFailure(['echo getfperm(v:null)'], 'E1174:')
enddef
def Test_getfsize()
assert_equal(-1, getfsize(""))
assert_equal(-1, getfsize(test_null_string()))
CheckDefExecFailure(['echo getfsize(true)'], 'E928:')
CheckDefExecFailure(['echo getfsize(v:null)'], 'E928:')
CheckDefExecFailure(['echo getfsize(true)'], 'E1174:')
CheckDefExecFailure(['echo getfsize(v:null)'], 'E1174:')
enddef
def Test_getftime()
assert_equal(-1, getftime(""))
assert_equal(-1, getftime(test_null_string()))
CheckDefExecFailure(['echo getftime(true)'], 'E928:')
CheckDefExecFailure(['echo getftime(v:null)'], 'E928:')
CheckDefExecFailure(['echo getftime(true)'], 'E1174:')
CheckDefExecFailure(['echo getftime(v:null)'], 'E1174:')
enddef
def Test_getftype()
assert_equal('', getftype(""))
assert_equal('', getftype(test_null_string()))
CheckDefExecFailure(['echo getftype(true)'], 'E928:')
CheckDefExecFailure(['echo getftype(v:null)'], 'E928:')
CheckDefExecFailure(['echo getftype(true)'], 'E1174:')
CheckDefExecFailure(['echo getftype(v:null)'], 'E1174:')
enddef
def Test_getqflist_return_type()

View File

@@ -797,6 +797,55 @@ def Test_silent_pattern()
bwipe!
enddef
def Test_useless_command_modifier()
g:maybe = true
var lines =<< trim END
if g:maybe
silent endif
END
CheckDefAndScriptFailure(lines, 'E1176:', 2)
lines =<< trim END
for i in [0]
silent endfor
END
CheckDefAndScriptFailure(lines, 'E1176:', 2)
lines =<< trim END
while g:maybe
silent endwhile
END
CheckDefAndScriptFailure(lines, 'E1176:', 2)
lines =<< trim END
silent try
finally
endtry
END
CheckDefAndScriptFailure(lines, 'E1176:', 1)
lines =<< trim END
try
silent catch
endtry
END
CheckDefAndScriptFailure(lines, 'E1176:', 2)
lines =<< trim END
try
silent finally
endtry
END
CheckDefAndScriptFailure(lines, 'E1176:', 2)
lines =<< trim END
try
finally
silent endtry
END
CheckDefAndScriptFailure(lines, 'E1176:', 3)
enddef
def Test_eval_command()
var from = 3
var to = 5
@@ -1056,6 +1105,27 @@ def Test_wincmd()
endif
assert_notequal(id1, win_getid())
close
split
var id = win_getid()
split
:2wincmd o
assert_equal(id, win_getid())
only
split
split
assert_equal(3, winnr('$'))
:2wincmd c
assert_equal(2, winnr('$'))
only
split
split
assert_equal(3, winnr('$'))
:2wincmd q
assert_equal(2, winnr('$'))
only
enddef
def Test_windo_missing_endif()

View File

@@ -1061,7 +1061,6 @@ def Test_disassemble_for_loop_eval()
'\d STORE -1 in $1\_s*' ..
'\d PUSHS "\["one", "two"\]"\_s*' ..
'\d BCALL eval(argc 1)\_s*' ..
'\d CHECKTYPE list<any> stack\[-1\]\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
'\d STORE $2\_s*' ..
'res ..= str\_s*' ..
@@ -1071,7 +1070,7 @@ def Test_disassemble_for_loop_eval()
'\d\+ CONCAT\_s*' ..
'\d\+ STORE $0\_s*' ..
'endfor\_s*' ..
'\d\+ JUMP -> 6\_s*' ..
'\d\+ JUMP -> 5\_s*' ..
'\d\+ DROP\_s*' ..
'return res\_s*' ..
'\d\+ LOAD $0\_s*' ..
@@ -1896,7 +1895,95 @@ def Test_silent()
'\d PUSHS "error"\_s*' ..
'\d ECHOERR 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
'\d RETURN 0',
'\d\+ RETURN 0',
res)
enddef
def s:SilentIf()
silent if 4 == g:five
silent elseif 4 == g:five
endif
enddef
def Test_silent_if()
var res = execute('disass s:SilentIf')
assert_match('<SNR>\d*_SilentIf\_s*' ..
'silent if 4 == g:five\_s*' ..
'\d\+ CMDMOD silent\_s*' ..
'\d\+ PUSHNR 4\_s*' ..
'\d\+ LOADG g:five\_s*' ..
'\d\+ COMPAREANY ==\_s*' ..
'\d\+ CMDMOD_REV\_s*' ..
'\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
'silent elseif 4 == g:five\_s*' ..
'\d\+ JUMP -> \d\+\_s*' ..
'\d\+ CMDMOD silent\_s*' ..
'\d\+ PUSHNR 4\_s*' ..
'\d\+ LOADG g:five\_s*' ..
'\d\+ COMPAREANY ==\_s*' ..
'\d\+ CMDMOD_REV\_s*' ..
'\d\+ JUMP_IF_FALSE -> \d\+\_s*' ..
'endif\_s*' ..
'\d\+ RETURN 0',
res)
enddef
def s:SilentFor()
silent for i in [0]
endfor
enddef
def Test_silent_for()
var res = execute('disass s:SilentFor')
assert_match('<SNR>\d*_SilentFor\_s*' ..
'silent for i in \[0\]\_s*' ..
'\d CMDMOD silent\_s*' ..
'\d STORE -1 in $0\_s*' ..
'\d PUSHNR 0\_s*' ..
'\d NEWLIST size 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
'5 FOR $0 -> 8\_s*' ..
'\d STORE $1\_s*' ..
'endfor\_s*' ..
'\d JUMP -> 5\_s*' ..
'8 DROP\_s*' ..
'\d RETURN 0\_s*',
res)
enddef
def s:SilentWhile()
silent while g:not
endwhile
enddef
def Test_silent_while()
var res = execute('disass s:SilentWhile')
assert_match('<SNR>\d*_SilentWhile\_s*' ..
'silent while g:not\_s*' ..
'0 CMDMOD silent\_s*' ..
'\d LOADG g:not\_s*' ..
'\d COND2BOOL\_s*' ..
'\d CMDMOD_REV\_s*' ..
'\d JUMP_IF_FALSE -> 6\_s*' ..
'endwhile\_s*' ..
'\d JUMP -> 0\_s*' ..
'6 RETURN 0\_s*',
res)
enddef
def s:SilentReturn(): string
silent return "done"
enddef
def Test_silent_return()
var res = execute('disass s:SilentReturn')
assert_match('<SNR>\d*_SilentReturn\_s*' ..
'silent return "done"\_s*' ..
'\d CMDMOD silent\_s*' ..
'\d PUSHS "done"\_s*' ..
'\d CMDMOD_REV\_s*' ..
'\d RETURN',
res)
enddef
@@ -1924,19 +2011,5 @@ def Test_profiled()
res)
enddef
def s:SilentReturn(): string
silent return "done"
enddef
def Test_silent_return()
var res = execute('disass s:SilentReturn')
assert_match('<SNR>\d*_SilentReturn\_s*' ..
'silent return "done"\_s*' ..
'\d CMDMOD silent\_s*' ..
'\d PUSHS "done"\_s*' ..
'\d CMDMOD_REV\_s*' ..
'\d RETURN',
res)
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -1961,6 +1961,11 @@ def Test_expr7_lambda_block()
return 'no'
})
assert_equal(['no', 'yes', 'no'], dll)
sandbox var Safe = (nr: number): number => {
return nr + 7
}
assert_equal(10, Safe(3))
END
CheckDefAndScriptSuccess(lines)
@@ -1968,6 +1973,34 @@ def Test_expr7_lambda_block()
map([1, 2], (k, v) => { redrawt })
END
CheckDefAndScriptFailure(lines, 'E488')
lines =<< trim END
var Func = (nr: int) => {
echo nr
}
END
CheckDefAndScriptFailure(lines, 'E1010', 1)
lines =<< trim END
var Func = (nr: number): int => {
return nr
}
END
CheckDefAndScriptFailure(lines, 'E1010', 1)
lines =<< trim END
var Func = (nr: number): int => {
return nr
END
CheckDefAndScriptFailure(lines, 'E1171', 1) # line nr is function start
lines =<< trim END
vim9script
var Func = (nr: number): int => {
var ll =<< ENDIT
nothing
END
CheckScriptFailure(lines, 'E1145: Missing heredoc end marker: ENDIT', 2)
enddef
def NewLambdaWithComments(): func

View File

@@ -86,6 +86,16 @@ def Test_endfunc_enddef()
enddef
END
CheckScriptFailure(lines, 'E1152:', 4)
lines =<< trim END
def Ok()
echo 'hello'
enddef | echo 'there'
def Bad()
echo 'hello'
enddef there
END
CheckScriptFailure(lines, 'E1173: Text found after enddef: there', 6)
enddef
def Test_missing_endfunc_enddef()
@@ -2353,6 +2363,29 @@ def Test_cmdmod_silent_restored()
delete(fname)
enddef
def Test_cmdmod_silent_nested()
var lines =<< trim END
vim9script
var result = ''
def Error()
result ..= 'Eb'
eval [][0]
result ..= 'Ea'
enddef
def Crash()
result ..= 'Cb'
sil! Error()
result ..= 'Ca'
enddef
Crash()
assert_equal('CbEbEaCa', result)
END
CheckScriptSuccess(lines)
enddef
def Test_dict_member_with_silent()
var lines =<< trim END
vim9script

View File

@@ -2263,6 +2263,13 @@ def Test_for_outside_of_function()
endfor
assert_equal(['', '0', '1', '2', '3'], getline(1, '$'))
bwipe!
var result = ''
for i in [1, 2, 3]
var loop = ' loop ' .. i
result ..= loop
endfor
assert_equal(' loop 1 loop 2 loop 3', result)
END
writefile(lines, 'Xvim9for.vim')
source Xvim9for.vim
@@ -2315,6 +2322,25 @@ def Test_for_loop()
res ..= n .. s
endfor
assert_equal('1a2b', res)
# loop over string
res = ''
for c in 'aéc̀d'
res ..= c .. '-'
endfor
assert_equal('a-é-c̀-d-', res)
res = ''
for c in ''
res ..= c .. '-'
endfor
assert_equal('', res)
res = ''
for c in test_null_string()
res ..= c .. '-'
endfor
assert_equal('', res)
enddef
def Test_for_loop_fails()
@@ -2326,10 +2352,17 @@ def Test_for_loop_fails()
CheckDefFailure(['var x = 5', 'for x in range(5)'], 'E1017:')
CheckScriptFailure(['def Func(arg: any)', 'for arg in range(5)', 'enddef', 'defcompile'], 'E1006:')
delfunc! g:Func
CheckDefFailure(['for i in "text"'], 'E1012:')
CheckDefFailure(['for i in xxx'], 'E1001:')
CheckDefFailure(['endfor'], 'E588:')
CheckDefFailure(['for i in range(3)', 'echo 3'], 'E170:')
# wrong type detected at compile time
CheckDefFailure(['for i in {a: 1}', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported')
# wrong type detected at runtime
g:adict = {a: 1}
CheckDefExecFailure(['for i in g:adict', 'echo 3', 'endfor'], 'E1177: For loop on dict not supported')
unlet g:adict
enddef
def Test_for_loop_script_var()

View File

@@ -7484,6 +7484,26 @@ func Test_trinary_expression()
call assert_equal(v:false, eval(string(v:false)))
endfunction
func Test_for_over_string()
let res = ''
for c in 'aéc̀d'
let res ..= c .. '-'
endfor
call assert_equal('a-é-c̀-d-', res)
let res = ''
for c in ''
let res ..= c .. '-'
endfor
call assert_equal('', res)
let res = ''
for c in test_null_string()
let res ..= c .. '-'
endfor
call assert_equal('', res)
endfunc
"-------------------------------------------------------------------------------
" Modelines {{{1
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker

View File

@@ -344,11 +344,14 @@ tv_get_float(typval_T *varp)
* Give an error and return FAIL unless "tv" is a string.
*/
int
check_for_string(typval_T *tv)
check_for_string(typval_T *tv, int arg)
{
if (tv->v_type != VAR_STRING)
{
emsg(_(e_stringreq));
if (arg > 0)
semsg(_(e_string_required_for_argument_nr), arg);
else
emsg(_(e_stringreq));
return FAIL;
}
return OK;
@@ -358,13 +361,16 @@ check_for_string(typval_T *tv)
* Give an error and return FAIL unless "tv" is a non-empty string.
*/
int
check_for_nonempty_string(typval_T *tv)
check_for_nonempty_string(typval_T *tv, int arg)
{
if (check_for_string(tv) == FAIL)
if (check_for_string(tv, arg) == FAIL)
return FAIL;
if (tv->vval.v_string == NULL || *tv->vval.v_string == NUL)
{
emsg(_(e_non_empty_string_required));
if (arg > 0)
semsg(_(e_non_empty_string_required_for_argument_nr), arg);
else
emsg(_(e_non_empty_string_required));
return FAIL;
}
return OK;

View File

@@ -731,13 +731,16 @@ get_function_body(
else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
nextcmd = line_arg;
else if (*p != NUL && *p != (vim9_function ? '#' : '"')
&& p_verbose > 0
&& eap->cmdidx != CMD_block)
give_warning2(eap->cmdidx == CMD_def
? (char_u *)_("W1001: Text found after :enddef: %s")
: (char_u *)_("W22: Text found after :endfunction: %s"),
p, TRUE);
if (nextcmd != NULL)
&& (vim9_function || p_verbose > 0))
{
if (eap->cmdidx == CMD_def)
semsg(_(e_text_found_after_enddef_str), p);
else
give_warning2((char_u *)
_("W22: Text found after :endfunction: %s"),
p, TRUE);
}
if (nextcmd != NULL && *skipwhite(nextcmd) != NUL)
{
// Another command follows. If the line came from "eap"
// we can simply point into it, otherwise we need to

View File

@@ -750,6 +750,38 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2660,
/**/
2659,
/**/
2658,
/**/
2657,
/**/
2656,
/**/
2655,
/**/
2654,
/**/
2653,
/**/
2652,
/**/
2651,
/**/
2650,
/**/
2649,
/**/
2648,
/**/
2647,
/**/
2646,
/**/
2645,
/**/
2644,
/**/

View File

@@ -402,12 +402,14 @@ struct dfunc_S {
// - ec_dfunc_idx: function index
// - ec_iidx: instruction index
// - ec_outer: stack used for closures
// - funclocal: function-local data
// - ec_frame_idx: previous frame index
#define STACK_FRAME_FUNC_OFF 0
#define STACK_FRAME_IIDX_OFF 1
#define STACK_FRAME_OUTER_OFF 2
#define STACK_FRAME_IDX_OFF 3
#define STACK_FRAME_SIZE 4
#define STACK_FRAME_FUNCLOCAL_OFF 3
#define STACK_FRAME_IDX_OFF 4
#define STACK_FRAME_SIZE 5
#ifdef DEFINE_VIM9_GLOBALS

View File

@@ -2142,11 +2142,7 @@ generate_cmdmods(cctx_T *cctx, cmdmod_T *cmod)
{
isn_T *isn;
if (cmod->cmod_flags != 0
|| cmod->cmod_split != 0
|| cmod->cmod_verbose != 0
|| cmod->cmod_tab != 0
|| cmod->cmod_filter_regmatch.regprog != NULL)
if (has_cmdmod(cmod))
{
cctx->ctx_has_cmdmod = TRUE;
@@ -2172,6 +2168,42 @@ generate_undo_cmdmods(cctx_T *cctx)
return OK;
}
static int
misplaced_cmdmod(cctx_T *cctx)
{
garray_T *instr = &cctx->ctx_instr;
if (cctx->ctx_has_cmdmod
&& ((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type
== ISN_CMDMOD)
{
emsg(_(e_misplaced_command_modifier));
return TRUE;
}
return FALSE;
}
/*
* Get the index of the current instruction.
* This compenstates for a preceding ISN_CMDMOD and ISN_PROF_START.
*/
static int
current_instr_idx(cctx_T *cctx)
{
garray_T *instr = &cctx->ctx_instr;
int idx = instr->ga_len;
if (cctx->ctx_has_cmdmod && ((isn_T *)instr->ga_data)[idx - 1]
.isn_type == ISN_CMDMOD)
--idx;
#ifdef FEAT_PROFILE
if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[idx - 1]
.isn_type == ISN_PROF_START)
--idx;
#endif
return idx;
}
#ifdef FEAT_PROFILE
static void
may_generate_prof_end(cctx_T *cctx, int prof_lnum)
@@ -6877,6 +6909,9 @@ compile_if(char_u *arg, cctx_T *cctx)
return NULL;
}
// CMDMOD_REV must come before the jump
generate_undo_cmdmods(cctx);
scope = new_scope(cctx, IF_SCOPE);
if (scope == NULL)
return NULL;
@@ -6937,24 +6972,36 @@ compile_elseif(char_u *arg, cctx_T *cctx)
if (scope->se_u.se_if.is_seen_skip_not)
{
// A previous block was executed, skip over expression and bail out.
// Do not count the "elseif" for profiling.
#ifdef FEAT_PROFILE
if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
.isn_type == ISN_PROF_START)
--instr->ga_len;
#endif
// Do not count the "elseif" for profiling and cmdmod
instr->ga_len = current_instr_idx(cctx);
skip_expr_cctx(&p, cctx);
return p;
}
if (cctx->ctx_skip == SKIP_UNKNOWN)
{
int moved_cmdmod = FALSE;
// Move any CMDMOD instruction to after the jump
if (((isn_T *)instr->ga_data)[instr->ga_len - 1].isn_type == ISN_CMDMOD)
{
if (ga_grow(instr, 1) == FAIL)
return NULL;
((isn_T *)instr->ga_data)[instr->ga_len] =
((isn_T *)instr->ga_data)[instr->ga_len - 1];
--instr->ga_len;
moved_cmdmod = TRUE;
}
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
JUMP_ALWAYS, cctx) == FAIL)
return NULL;
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
isn->isn_arg.jump.jump_where = instr->ga_len;
if (moved_cmdmod)
++instr->ga_len;
}
// compile "expr"; if we know it evaluates to FALSE skip the block
@@ -7007,6 +7054,9 @@ compile_elseif(char_u *arg, cctx_T *cctx)
if (bool_on_stack(cctx) == FAIL)
return NULL;
// CMDMOD_REV must come before the jump
generate_undo_cmdmods(cctx);
// "where" is set when ":elseif", "else" or ":endif" is found
scope->se_u.se_if.is_if_label = instr->ga_len;
generate_JUMP(cctx, JUMP_IF_FALSE, 0);
@@ -7090,6 +7140,9 @@ compile_endif(char_u *arg, cctx_T *cctx)
garray_T *instr = &cctx->ctx_instr;
isn_T *isn;
if (misplaced_cmdmod(cctx))
return NULL;
if (scope == NULL || scope->se_type != IF_SCOPE)
{
emsg(_(e_endif_without_if));
@@ -7160,7 +7213,6 @@ compile_for(char_u *arg_start, cctx_T *cctx)
int var_count = 0;
int semicolon = FALSE;
size_t varlen;
garray_T *instr = &cctx->ctx_instr;
garray_T *stack = &cctx->ctx_type_stack;
scope_T *scope;
lvar_T *loop_lvar; // loop iteration variable
@@ -7212,11 +7264,15 @@ compile_for(char_u *arg_start, cctx_T *cctx)
}
arg_end = arg;
// Now that we know the type of "var", check that it is a list, now or at
// runtime.
// If we know the type of "var" and it is a not a list or string we can
// give an error now.
vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (need_type(vartype, &t_list_any, -1, 0, cctx, FALSE, FALSE) == FAIL)
if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING
&& vartype->tt_type != VAR_ANY)
{
// TODO: support Blob
semsg(_(e_for_loop_on_str_not_supported),
vartype_name(vartype->tt_type));
drop_scope(cctx);
return NULL;
}
@@ -7230,8 +7286,11 @@ compile_for(char_u *arg_start, cctx_T *cctx)
item_type = vartype->tt_member->tt_member;
}
// CMDMOD_REV must come before the FOR instruction
generate_undo_cmdmods(cctx);
// "for_end" is set when ":endfor" is found
scope->se_u.se_for.fs_top_label = instr->ga_len;
scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
generate_FOR(cctx, loop_lvar->lv_idx);
arg = arg_start;
@@ -7333,6 +7392,9 @@ compile_endfor(char_u *arg, cctx_T *cctx)
forscope_T *forscope;
isn_T *isn;
if (misplaced_cmdmod(cctx))
return NULL;
if (scope == NULL || scope->se_type != FOR_SCOPE)
{
emsg(_(e_for));
@@ -7376,20 +7438,14 @@ compile_endfor(char_u *arg, cctx_T *cctx)
compile_while(char_u *arg, cctx_T *cctx)
{
char_u *p = arg;
garray_T *instr = &cctx->ctx_instr;
scope_T *scope;
scope = new_scope(cctx, WHILE_SCOPE);
if (scope == NULL)
return NULL;
// "endwhile" jumps back here, one before when profiling
scope->se_u.se_while.ws_top_label = instr->ga_len;
#ifdef FEAT_PROFILE
if (cctx->ctx_profiling && ((isn_T *)instr->ga_data)[instr->ga_len - 1]
.isn_type == ISN_PROF_START)
--scope->se_u.se_while.ws_top_label;
#endif
// "endwhile" jumps back here, one before when profiling or using cmdmods
scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);
// compile "expr"
if (compile_expr0(&p, cctx) == FAIL)
@@ -7403,6 +7459,9 @@ compile_while(char_u *arg, cctx_T *cctx)
if (bool_on_stack(cctx) == FAIL)
return FAIL;
// CMDMOD_REV must come before the jump
generate_undo_cmdmods(cctx);
// "while_end" is set when ":endwhile" is found
if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
JUMP_IF_FALSE, cctx) == FAIL)
@@ -7420,6 +7479,8 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
scope_T *scope = cctx->ctx_scope;
garray_T *instr = &cctx->ctx_instr;
if (misplaced_cmdmod(cctx))
return NULL;
if (scope == NULL || scope->se_type != WHILE_SCOPE)
{
emsg(_(e_while));
@@ -7584,6 +7645,9 @@ compile_try(char_u *arg, cctx_T *cctx)
scope_T *try_scope;
scope_T *scope;
if (misplaced_cmdmod(cctx))
return NULL;
// scope that holds the jumps that go to catch/finally/endtry
try_scope = new_scope(cctx, TRY_SCOPE);
if (try_scope == NULL)
@@ -7624,6 +7688,9 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
char_u *p;
isn_T *isn;
if (misplaced_cmdmod(cctx))
return NULL;
// end block scope from :try or :catch
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
compile_endblock(cctx);
@@ -7736,6 +7803,9 @@ compile_finally(char_u *arg, cctx_T *cctx)
isn_T *isn;
int this_instr;
if (misplaced_cmdmod(cctx))
return NULL;
// end block scope from :try or :catch
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
compile_endblock(cctx);
@@ -7794,6 +7864,9 @@ compile_endtry(char_u *arg, cctx_T *cctx)
garray_T *instr = &cctx->ctx_instr;
isn_T *try_isn;
if (misplaced_cmdmod(cctx))
return NULL;
// end block scope from :catch or :finally
if (scope != NULL && scope->se_type == BLOCK_SCOPE)
compile_endblock(cctx);

View File

@@ -154,6 +154,15 @@ exe_newlist(int count, ectx_T *ectx)
return OK;
}
// Data local to a function.
// On a function call, if not empty, is saved on the stack and restored when
// returning.
typedef struct {
int floc_restore_cmdmod;
cmdmod_T floc_save_cmdmod;
int floc_restore_cmdmod_stacklen;
} funclocal_T;
/*
* Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the
@@ -170,16 +179,22 @@ exe_newlist(int count, ectx_T *ectx)
* - reserved space for local variables
*/
static int
call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
call_dfunc(
int cdf_idx,
partial_T *pt,
int argcount_arg,
funclocal_T *funclocal,
ectx_T *ectx)
{
int argcount = argcount_arg;
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
ufunc_T *ufunc = dfunc->df_ufunc;
int arg_to_add;
int vararg_count = 0;
int varcount;
int idx;
estack_T *entry;
int argcount = argcount_arg;
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data) + cdf_idx;
ufunc_T *ufunc = dfunc->df_ufunc;
int arg_to_add;
int vararg_count = 0;
int varcount;
int idx;
estack_T *entry;
funclocal_T *floc = NULL;
if (dfunc->df_deleted)
{
@@ -267,6 +282,16 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
if (funcdepth_increment() == FAIL)
return FAIL;
// Only make a copy of funclocal if it contains something to restore.
if (funclocal->floc_restore_cmdmod)
{
floc = ALLOC_ONE(funclocal_T);
if (floc == NULL)
return FAIL;
*floc = *funclocal;
funclocal->floc_restore_cmdmod = FALSE;
}
// Move the vararg-list to below the missing optional arguments.
if (vararg_count > 0 && arg_to_add > 0)
*STACK_TV_BOT(arg_to_add - 1) = *STACK_TV_BOT(-1);
@@ -280,6 +305,7 @@ call_dfunc(int cdf_idx, partial_T *pt, int argcount_arg, ectx_T *ectx)
STACK_TV_BOT(STACK_FRAME_FUNC_OFF)->vval.v_number = ectx->ec_dfunc_idx;
STACK_TV_BOT(STACK_FRAME_IIDX_OFF)->vval.v_number = ectx->ec_iidx;
STACK_TV_BOT(STACK_FRAME_OUTER_OFF)->vval.v_string = (void *)ectx->ec_outer;
STACK_TV_BOT(STACK_FRAME_FUNCLOCAL_OFF)->vval.v_string = (void *)floc;
STACK_TV_BOT(STACK_FRAME_IDX_OFF)->vval.v_number = ectx->ec_frame_idx;
ectx->ec_frame_idx = ectx->ec_stack.ga_len;
@@ -530,7 +556,7 @@ funcstack_check_refcount(funcstack_T *funcstack)
* Return from the current function.
*/
static int
func_return(ectx_T *ectx)
func_return(funclocal_T *funclocal, ectx_T *ectx)
{
int idx;
int ret_idx;
@@ -543,6 +569,7 @@ func_return(ectx_T *ectx)
+ STACK_FRAME_FUNC_OFF)->vval.v_number;
dfunc_T *prev_dfunc = ((dfunc_T *)def_functions.ga_data)
+ prev_dfunc_idx;
funclocal_T *floc;
#ifdef FEAT_PROFILE
if (do_profiling == PROF_YES)
@@ -592,11 +619,21 @@ func_return(ectx_T *ectx)
+ STACK_FRAME_IIDX_OFF)->vval.v_number;
ectx->ec_outer = (void *)STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_OUTER_OFF)->vval.v_string;
floc = (void *)STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_FUNCLOCAL_OFF)->vval.v_string;
// restoring ec_frame_idx must be last
ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx
+ STACK_FRAME_IDX_OFF)->vval.v_number;
ectx->ec_instr = INSTRUCTIONS(prev_dfunc);
if (floc == NULL)
funclocal->floc_restore_cmdmod = FALSE;
else
{
*funclocal = *floc;
vim_free(floc);
}
if (ret_idx > 0)
{
// Reset the stack to the position before the call, with a spot for the
@@ -690,6 +727,7 @@ call_ufunc(
ufunc_T *ufunc,
partial_T *pt,
int argcount,
funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{
@@ -729,7 +767,7 @@ call_ufunc(
iptr->isn_arg.dfunc.cdf_idx = ufunc->uf_dfunc_idx;
iptr->isn_arg.dfunc.cdf_argcount = argcount;
}
return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, ectx);
return call_dfunc(ufunc->uf_dfunc_idx, pt, argcount, funclocal, ectx);
}
if (call_prepare(argcount, argvars, ectx) == FAIL)
@@ -757,6 +795,21 @@ call_ufunc(
return OK;
}
/*
* If command modifiers were applied restore them.
*/
static void
may_restore_cmdmod(funclocal_T *funclocal)
{
if (funclocal->floc_restore_cmdmod)
{
cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod);
cmdmod = funclocal->floc_save_cmdmod;
funclocal->floc_restore_cmdmod = FALSE;
}
}
/*
* Return TRUE if an error was given or CTRL-C was pressed.
*/
@@ -773,7 +826,12 @@ vim9_aborting(int prev_called_emsg)
* Returns FAIL if not found without an error message.
*/
static int
call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
call_by_name(
char_u *name,
int argcount,
funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{
ufunc_T *ufunc;
@@ -824,14 +882,18 @@ call_by_name(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
}
}
return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
return call_ufunc(ufunc, NULL, argcount, funclocal, ectx, iptr);
}
return FAIL;
}
static int
call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
call_partial(
typval_T *tv,
int argcount_arg,
funclocal_T *funclocal,
ectx_T *ectx)
{
int argcount = argcount_arg;
char_u *name = NULL;
@@ -860,7 +922,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
}
if (pt->pt_func != NULL)
return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
return call_ufunc(pt->pt_func, pt, argcount, funclocal, ectx, NULL);
name = pt->pt_name;
}
@@ -878,7 +940,7 @@ call_partial(typval_T *tv, int argcount_arg, ectx_T *ectx)
if (error != FCERR_NONE)
res = FAIL;
else
res = call_by_name(fname, argcount, ectx, NULL);
res = call_by_name(fname, argcount, funclocal, ectx, NULL);
vim_free(tofree);
}
@@ -1005,13 +1067,22 @@ char_from_string(char_u *str, varnumber_T index)
return NULL;
slen = STRLEN(str);
// do the same as for a list: a negative index counts from the end
// Do the same as for a list: a negative index counts from the end.
// Optimization to check the first byte to be below 0x80 (and no composing
// character follows) makes this a lot faster.
if (index < 0)
{
int clen = 0;
for (nbyte = 0; nbyte < slen; ++clen)
nbyte += mb_ptr2len(str + nbyte);
{
if (str[nbyte] < 0x80 && str[nbyte + 1] < 0x80)
++nbyte;
else if (enc_utf8)
nbyte += utfc_ptr2len(str + nbyte);
else
nbyte += mb_ptr2len(str + nbyte);
}
nchar = clen + index;
if (nchar < 0)
// unlike list: index out of range results in empty string
@@ -1019,7 +1090,14 @@ char_from_string(char_u *str, varnumber_T index)
}
for (nbyte = 0; nchar > 0 && nbyte < slen; --nchar)
nbyte += mb_ptr2len(str + nbyte);
{
if (str[nbyte] < 0x80 && str[nbyte + 1] < 0x80)
++nbyte;
else if (enc_utf8)
nbyte += utfc_ptr2len(str + nbyte);
else
nbyte += mb_ptr2len(str + nbyte);
}
if (nbyte >= slen)
return NULL;
return vim_strnsave(str + nbyte, mb_ptr2len(str + nbyte));
@@ -1125,12 +1203,17 @@ get_script_svar(scriptref_T *sref, ectx_T *ectx)
* "iptr" can be used to replace the instruction with a more efficient one.
*/
static int
call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
call_eval_func(
char_u *name,
int argcount,
funclocal_T *funclocal,
ectx_T *ectx,
isn_T *iptr)
{
int called_emsg_before = called_emsg;
int res;
res = call_by_name(name, argcount, ectx, iptr);
res = call_by_name(name, argcount, funclocal, ectx, iptr);
if (res == FAIL && called_emsg == called_emsg_before)
{
dictitem_T *v;
@@ -1146,7 +1229,7 @@ call_eval_func(char_u *name, int argcount, ectx_T *ectx, isn_T *iptr)
semsg(_(e_unknownfunc), name);
return FAIL;
}
return call_partial(&v->di_tv, argcount, ectx);
return call_partial(&v->di_tv, argcount, funclocal, ectx);
}
return res;
}
@@ -1222,9 +1305,7 @@ call_def_function(
int save_suppress_errthrow = suppress_errthrow;
msglist_T **saved_msg_list = NULL;
msglist_T *private_msg_list = NULL;
cmdmod_T save_cmdmod;
int restore_cmdmod = FALSE;
int restore_cmdmod_stacklen = 0;
funclocal_T funclocal;
int save_emsg_silent_def = emsg_silent_def;
int save_did_emsg_def = did_emsg_def;
int trylevel_at_start = trylevel;
@@ -1268,6 +1349,7 @@ call_def_function(
if (funcdepth_increment() == FAIL)
return FAIL;
CLEAR_FIELD(funclocal);
CLEAR_FIELD(ectx);
ectx.ec_dfunc_idx = ufunc->uf_dfunc_idx;
ga_init2(&ectx.ec_stack, sizeof(typval_T), 500);
@@ -1488,7 +1570,7 @@ call_def_function(
goto done;
}
if (func_return(&ectx) == FAIL)
if (func_return(&funclocal, &ectx) == FAIL)
goto failed;
}
continue;
@@ -2467,9 +2549,11 @@ call_def_function(
// call a :def function
case ISN_DCALL:
SOURCING_LNUM = iptr->isn_lnum;
if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx, NULL,
iptr->isn_arg.dfunc.cdf_argcount,
&ectx) == FAIL)
if (call_dfunc(iptr->isn_arg.dfunc.cdf_idx,
NULL,
iptr->isn_arg.dfunc.cdf_argcount,
&funclocal,
&ectx) == FAIL)
goto on_error;
break;
@@ -2502,7 +2586,8 @@ call_def_function(
partial_tv = *STACK_TV_BOT(0);
tv = &partial_tv;
}
r = call_partial(tv, pfunc->cpf_argcount, &ectx);
r = call_partial(tv, pfunc->cpf_argcount,
&funclocal, &ectx);
if (tv == &partial_tv)
clear_tv(&partial_tv);
if (r == FAIL)
@@ -2525,8 +2610,8 @@ call_def_function(
cufunc_T *cufunc = &iptr->isn_arg.ufunc;
SOURCING_LNUM = iptr->isn_lnum;
if (call_eval_func(cufunc->cuf_name,
cufunc->cuf_argcount, &ectx, iptr) == FAIL)
if (call_eval_func(cufunc->cuf_name, cufunc->cuf_argcount,
&funclocal, &ectx, iptr) == FAIL)
goto on_error;
}
break;
@@ -2656,33 +2741,76 @@ call_def_function(
// top of a for loop
case ISN_FOR:
{
list_T *list = STACK_TV_BOT(-1)->vval.v_list;
typval_T *ltv = STACK_TV_BOT(-1);
typval_T *idxtv =
STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
// push the next item from the list
if (GA_GROW(&ectx.ec_stack, 1) == FAIL)
goto failed;
++idxtv->vval.v_number;
if (list == NULL || idxtv->vval.v_number >= list->lv_len)
// past the end of the list, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
else if (list->lv_first == &range_list_item)
if (ltv->v_type == VAR_LIST)
{
// non-materialized range() list
tv = STACK_TV_BOT(0);
tv->v_type = VAR_NUMBER;
tv->v_lock = 0;
tv->vval.v_number = list_find_nr(
list_T *list = ltv->vval.v_list;
// push the next item from the list
++idxtv->vval.v_number;
if (list == NULL
|| idxtv->vval.v_number >= list->lv_len)
{
// past the end of the list, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&funclocal);
}
else if (list->lv_first == &range_list_item)
{
// non-materialized range() list
tv = STACK_TV_BOT(0);
tv->v_type = VAR_NUMBER;
tv->v_lock = 0;
tv->vval.v_number = list_find_nr(
list, idxtv->vval.v_number, NULL);
++ectx.ec_stack.ga_len;
++ectx.ec_stack.ga_len;
}
else
{
listitem_T *li = list_find(list,
idxtv->vval.v_number);
copy_tv(&li->li_tv, STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
}
}
else if (ltv->v_type == VAR_STRING)
{
char_u *str = ltv->vval.v_string;
int len = str == NULL ? 0 : (int)STRLEN(str);
// Push the next character from the string. The index
// is for the last byte of the previous character.
++idxtv->vval.v_number;
if (idxtv->vval.v_number >= len)
{
// past the end of the string, jump to "endfor"
ectx.ec_iidx = iptr->isn_arg.forloop.for_end;
may_restore_cmdmod(&funclocal);
}
else
{
int clen = mb_ptr2len(str + idxtv->vval.v_number);
tv = STACK_TV_BOT(0);
tv->v_type = VAR_STRING;
tv->vval.v_string = vim_strnsave(
str + idxtv->vval.v_number, clen);
++ectx.ec_stack.ga_len;
idxtv->vval.v_number += clen - 1;
}
}
else
{
listitem_T *li = list_find(list, idxtv->vval.v_number);
copy_tv(&li->li_tv, STACK_TV_BOT(0));
++ectx.ec_stack.ga_len;
// TODO: support Blob
semsg(_(e_for_loop_on_str_not_supported),
vartype_name(ltv->v_type));
goto failed;
}
}
break;
@@ -2701,9 +2829,12 @@ call_def_function(
CLEAR_POINTER(trycmd);
trycmd->tcd_frame_idx = ectx.ec_frame_idx;
trycmd->tcd_stack_len = ectx.ec_stack.ga_len;
trycmd->tcd_catch_idx = iptr->isn_arg.try.try_ref->try_catch;
trycmd->tcd_finally_idx = iptr->isn_arg.try.try_ref->try_finally;
trycmd->tcd_endtry_idx = iptr->isn_arg.try.try_ref->try_endtry;
trycmd->tcd_catch_idx =
iptr->isn_arg.try.try_ref->try_catch;
trycmd->tcd_finally_idx =
iptr->isn_arg.try.try_ref->try_finally;
trycmd->tcd_endtry_idx =
iptr->isn_arg.try.try_ref->try_endtry;
}
break;
@@ -2728,13 +2859,7 @@ call_def_function(
{
garray_T *trystack = &ectx.ec_trystack;
if (restore_cmdmod)
{
cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod;
restore_cmdmod = FALSE;
}
may_restore_cmdmod(&funclocal);
if (trystack->ga_len > 0)
{
trycmd_T *trycmd = ((trycmd_T *)trystack->ga_data)
@@ -3649,9 +3774,9 @@ call_def_function(
break;
case ISN_CMDMOD:
save_cmdmod = cmdmod;
restore_cmdmod = TRUE;
restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
funclocal.floc_save_cmdmod = cmdmod;
funclocal.floc_restore_cmdmod = TRUE;
funclocal.floc_restore_cmdmod_stacklen = ectx.ec_stack.ga_len;
cmdmod = *iptr->isn_arg.cmdmod.cf_cmdmod;
apply_cmdmod(&cmdmod);
break;
@@ -3660,8 +3785,8 @@ call_def_function(
// filter regprog is owned by the instruction, don't free it
cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod;
restore_cmdmod = FALSE;
cmdmod = funclocal.floc_save_cmdmod;
funclocal.floc_restore_cmdmod = FALSE;
break;
case ISN_UNPACK:
@@ -3790,7 +3915,7 @@ func_return:
if (ectx.ec_frame_idx == initial_frame_idx)
goto done;
if (func_return(&ectx) == FAIL)
if (func_return(&funclocal, &ectx) == FAIL)
// only fails when out of memory
goto failed;
continue;
@@ -3805,9 +3930,10 @@ on_error:
// If a sequence of instructions causes an error while ":silent!"
// was used, restore the stack length and jump ahead to restoring
// the cmdmod.
if (restore_cmdmod)
if (funclocal.floc_restore_cmdmod)
{
while (ectx.ec_stack.ga_len > restore_cmdmod_stacklen)
while (ectx.ec_stack.ga_len
> funclocal.floc_restore_cmdmod_stacklen)
{
--ectx.ec_stack.ga_len;
clear_tv(STACK_TV_BOT(0));
@@ -3834,7 +3960,7 @@ done:
failed:
// When failed need to unwind the call stack.
while (ectx.ec_frame_idx != initial_frame_idx)
func_return(&ectx);
func_return(&funclocal, &ectx);
// Deal with any remaining closures, they may be in use somewhere.
if (ectx.ec_funcrefs.ga_len > 0)
@@ -3862,11 +3988,11 @@ failed:
}
msg_list = saved_msg_list;
if (restore_cmdmod)
if (funclocal.floc_restore_cmdmod)
{
cmdmod.cmod_filter_regmatch.regprog = NULL;
undo_cmdmod(&cmdmod);
cmdmod = save_cmdmod;
cmdmod = funclocal.floc_save_cmdmod;
}
emsg_silent_def = save_emsg_silent_def;
did_emsg_def += save_did_emsg_def;

View File

@@ -750,11 +750,10 @@ cmd_with_count(
size_t bufsize,
long Prenum)
{
size_t len = STRLEN(cmd);
STRCPY(bufp, cmd);
if (Prenum > 0)
vim_snprintf((char *)bufp + len, bufsize - len, "%ld", Prenum);
vim_snprintf((char *)bufp, bufsize, "%s %ld", cmd, Prenum);
else
STRCPY(bufp, cmd);
}
/*