Merge remote-tracking branch 'vim/master'

This commit is contained in:
Yee Cheng Chin
2022-09-16 18:15:08 -07:00
36 changed files with 624 additions and 307 deletions
+10 -4
View File
@@ -195,7 +195,7 @@ foldlevel({lnum}) Number fold level at {lnum}
foldtext() String line displayed for closed fold
foldtextresult({lnum}) String text for closed fold at {lnum}
foreground() Number bring the Vim window to the foreground
fullcommand({name}) String get full command from {name}
fullcommand({name} [, {vim9}]) String get full command from {name}
funcref({name} [, {arglist}] [, {dict}])
Funcref reference to function {name}
function({name} [, {arglist}] [, {dict}])
@@ -2967,14 +2967,20 @@ foreground() Move the Vim window to the foreground. Useful when sent from
{only in the Win32, Motif and GTK GUI versions and the
Win32 console version}
fullcommand({name}) *fullcommand()*
fullcommand({name} [, {vim9}]) *fullcommand()*
Get the full command name from a short abbreviated command
name; see |20.2| for details on command abbreviations.
The string argument {name} may start with a `:` and can
include a [range], these are skipped and not returned.
Returns an empty string if a command doesn't exist or if it's
ambiguous (for user-defined commands).
Returns an empty string if a command doesn't exist, if it's
ambiguous (for user-defined commands) or cannot be shortened
this way. |vim9-no-shorten|
Without the {vim9} argument uses the current script version.
If {vim9} is present and FALSE then legacy script rules are
used. When {vim9} is present and TRUE then Vim9 rules are
used, e.g. "en" is not a short form of "endif".
For example `fullcommand('s')`, `fullcommand('sub')`,
`fullcommand(':%substitute')` all return "substitute".
+3
View File
@@ -2108,6 +2108,9 @@ au BufNewFile,BufRead */.config/upstart/*.override setf upstart
" Vala
au BufNewFile,BufRead *.vala setf vala
" VDF
au BufNewFile,BufRead *.vdf setf vdf
" VDM
au BufRead,BufNewFile *.vdmpp,*.vpp setf vdmpp
au BufRead,BufNewFile *.vdmrt setf vdmrt
+1 -1
View File
@@ -515,7 +515,7 @@ call <SID>AddOption("splitbelow", gettext("a new window is put below the current
call <SID>BinOptionG("sb", &sb)
call <SID>AddOption("splitright", gettext("a new window is put right of the current one"))
call <SID>BinOptionG("spr", &spr)
call <SID>AddOption("splitscroll", gettext("determines scroll behavior when spliting windows"))
call <SID>AddOption("splitscroll", gettext("determines scroll behavior for split windows"))
call <SID>BinOptionG("spsc", &spsc)
call <SID>AddOption("scrollbind", gettext("this window scrolls together with other bound windows"))
call append("$", "\t" .. s:local_to_window)
+1 -1
View File
@@ -4258,7 +4258,7 @@ objects/vim9type.o: vim9type.c vim.h protodef.h auto/config.h feature.h os_unix.
os_mac.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
libvterm/include/vterm_keycodes.h alloc.h ex_cmds.h spell.h proto.h \
globals.h errors.h
globals.h errors.h vim9.h
objects/viminfo.o: viminfo.c vim.h protodef.h auto/config.h feature.h os_unix.h \
os_mac.h ascii.h keymap.h termdefs.h macros.h option.h beval.h \
proto/gui_beval.pro structs.h regexp.h gui.h libvterm/include/vterm.h \
+1 -1
View File
@@ -1853,7 +1853,7 @@ static funcentry_T global_functions[] =
ret_string, f_foldtextresult},
{"foreground", 0, 0, 0, NULL,
ret_void, f_foreground},
{"fullcommand", 1, 1, FEARG_1, arg1_string,
{"fullcommand", 1, 2, FEARG_1, arg2_string_bool,
ret_string, f_fullcommand},
{"funcref", 1, 3, FEARG_1, arg3_any_list_dict,
ret_func_unknown, f_funcref},
+23 -7
View File
@@ -4054,20 +4054,31 @@ cmd_exists(char_u *name)
void
f_fullcommand(typval_T *argvars, typval_T *rettv)
{
exarg_T ea;
char_u *name;
char_u *p;
exarg_T ea;
char_u *name;
char_u *p;
int vim9script = in_vim9script();
int save_cmod_flags = cmdmod.cmod_flags;
rettv->v_type = VAR_STRING;
rettv->vval.v_string = NULL;
if (in_vim9script() && check_for_string_arg(argvars, 0) == FAIL)
if (in_vim9script()
&& (check_for_string_arg(argvars, 0) == FAIL
|| check_for_opt_bool_arg(argvars, 1) == FAIL))
return;
name = argvars[0].vval.v_string;
if (name == NULL)
return;
if (argvars[1].v_type != VAR_UNKNOWN)
{
vim9script = tv_get_bool(&argvars[1]);
cmdmod.cmod_flags &= ~(CMOD_VIM9CMD | CMOD_LEGACY);
cmdmod.cmod_flags |= vim9script ? CMOD_VIM9CMD : CMOD_LEGACY;
}
while (*name == ':')
name++;
name = skip_range(name, TRUE, NULL);
@@ -4075,10 +4086,13 @@ f_fullcommand(typval_T *argvars, typval_T *rettv)
ea.cmd = (*name == '2' || *name == '3') ? name + 1 : name;
ea.cmdidx = (cmdidx_T)0;
ea.addr_count = 0;
++emsg_silent; // don't complain about using "en" in Vim9 script
p = find_ex_command(&ea, NULL, NULL, NULL);
--emsg_silent;
if (p == NULL || ea.cmdidx == CMD_SIZE)
return;
if (in_vim9script())
goto theend;
if (vim9script)
{
int res;
@@ -4087,12 +4101,14 @@ f_fullcommand(typval_T *argvars, typval_T *rettv)
--emsg_silent;
if (res == FAIL)
return;
goto theend;
}
rettv->vval.v_string = vim_strsave(IS_USER_CMDIDX(ea.cmdidx)
? get_user_command_name(ea.useridx, ea.cmdidx)
: cmdnames[ea.cmdidx].cmd_name);
theend:
cmdmod.cmod_flags = save_cmod_flags;
}
#endif
+19 -9
View File
@@ -470,19 +470,29 @@ check_top_offset(void)
return FALSE;
}
/*
* Update w_curswant.
*/
void
update_curswant_force(void)
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
curwin->w_set_curswant = FALSE;
}
/*
* Update w_curswant if w_set_curswant is set.
*/
void
update_curswant(void)
{
if (curwin->w_set_curswant)
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
curwin->w_set_curswant = FALSE;
}
update_curswant_force();
}
/*
+29 -19
View File
@@ -5503,9 +5503,8 @@ nv_visual(cmdarg_T *cap)
{
if (resel_VIsual_line_count <= 1)
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol
+ resel_VIsual_vcol * cap->count0 - 1;
update_curswant_force();
curwin->w_curswant += resel_VIsual_vcol * cap->count0 - 1;
}
else
curwin->w_curswant = resel_VIsual_vcol;
@@ -5518,9 +5517,8 @@ nv_visual(cmdarg_T *cap)
}
else if (VIsual_mode == Ctrl_V)
{
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol
+ resel_VIsual_vcol * cap->count0 - 1;
update_curswant_force();
curwin->w_curswant += + resel_VIsual_vcol * cap->count0 - 1;
coladvance(curwin->w_curswant);
}
else
@@ -5747,13 +5745,19 @@ nv_g_home_m_cmd(cmdarg_T *cap)
cap->oap->inclusive = FALSE;
if (curwin->w_p_wrap && curwin->w_width != 0)
{
int width1 = curwin->w_width - curwin_col_off();
int width2 = width1 + curwin_col_off2();
int width1 = curwin->w_width - curwin_col_off();
int width2 = width1 + curwin_col_off2();
int virtcol;
validate_virtcol();
virtcol = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
i = 0;
if (curwin->w_virtcol >= (colnr_T)width1 && width2 > 0)
i = (curwin->w_virtcol - width1) / width2 * width2 + width1;
if (virtcol >= (colnr_T)width1 && width2 > 0)
i = (virtcol - width1) / width2 * width2 + width1;
}
else
i = curwin->w_leftcol;
@@ -5827,24 +5831,32 @@ nv_g_dollar_cmd(cmdarg_T *cap)
{
int width1 = curwin->w_width - col_off;
int width2 = width1 + curwin_col_off2();
int virtcol;
validate_virtcol();
virtcol = curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
;
i = width1 - 1;
if (curwin->w_virtcol >= (colnr_T)width1)
i += ((curwin->w_virtcol - width1) / width2 + 1)
if (virtcol >= (colnr_T)width1)
i += ((virtcol - width1) / width2 + 1)
* width2;
coladvance((colnr_T)i);
// Make sure we stick in this column.
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol;
curwin->w_set_curswant = FALSE;
update_curswant_force();
if (curwin->w_cursor.col > 0 && curwin->w_p_wrap)
{
// Check for landing on a character that got split at
// the end of the line. We do not want to advance to
// the next screen line.
if (curwin->w_virtcol > (colnr_T)i)
if (curwin->w_virtcol
#ifdef FEAT_PROP_POPUP
- curwin->w_virtcol_first_char
#endif
> (colnr_T)i)
--curwin->w_cursor.col;
}
}
@@ -5872,9 +5884,7 @@ nv_g_dollar_cmd(cmdarg_T *cap)
}
// Make sure we stick in this column.
validate_virtcol();
curwin->w_curswant = curwin->w_virtcol;
curwin->w_set_curswant = FALSE;
update_curswant_force();
}
}
+10 -2
View File
@@ -1173,6 +1173,8 @@ op_replace(oparg_T *oap, int c)
while (LTOREQ_POS(curwin->w_cursor, oap->end))
{
int done = FALSE;
n = gchar_cursor();
if (n != NUL)
{
@@ -1186,6 +1188,7 @@ op_replace(oparg_T *oap, int c)
if (curwin->w_cursor.lnum == oap->end.lnum)
oap->end.col += new_byte_len - old_byte_len;
replace_character(c);
done = TRUE;
}
else
{
@@ -1204,10 +1207,15 @@ op_replace(oparg_T *oap, int c)
if (curwin->w_cursor.lnum == oap->end.lnum)
getvpos(&oap->end, end_vcol);
}
PBYTE(curwin->w_cursor, c);
// with "coladd" set may move to just after a TAB
if (gchar_cursor() != NUL)
{
PBYTE(curwin->w_cursor, c);
done = TRUE;
}
}
}
else if (virtual_op && curwin->w_cursor.lnum == oap->end.lnum)
if (!done && virtual_op && curwin->w_cursor.lnum == oap->end.lnum)
{
int virtcols = oap->end.coladd;
+1
View File
@@ -2,6 +2,7 @@
void redraw_for_cursorline(win_T *wp);
void update_topline_redraw(void);
void update_topline(void);
void update_curswant_force(void);
void update_curswant(void);
void check_cursor_moved(win_T *wp);
void changed_window_setting(void);
+1 -1
View File
@@ -16,7 +16,7 @@ int func_is_global(ufunc_T *ufunc);
int func_requires_g_prefix(ufunc_T *ufunc);
int func_name_refcount(char_u *name);
void func_clear_free(ufunc_T *fp, int force);
int copy_func(char_u *lambda, char_u *global, ectx_T *ectx);
int copy_lambda_to_global_func(char_u *lambda, char_u *global, short loop_var_idx, short loop_var_count, ectx_T *ectx);
int funcdepth_increment(void);
void funcdepth_decrement(void);
int funcdepth_get(void);
+2
View File
@@ -11,6 +11,8 @@ char_u *compile_for(char_u *arg_start, cctx_T *cctx);
char_u *compile_endfor(char_u *arg, cctx_T *cctx);
char_u *compile_while(char_u *arg, cctx_T *cctx);
char_u *compile_endwhile(char_u *arg, cctx_T *cctx);
short get_loop_var_info(cctx_T *cctx, short *loop_var_idx);
int get_loop_var_idx(cctx_T *cctx);
char_u *compile_continue(char_u *arg, cctx_T *cctx);
char_u *compile_break(char_u *arg, cctx_T *cctx);
char_u *compile_block(char_u *arg, cctx_T *cctx);
+1 -1
View File
@@ -9,7 +9,7 @@ void restore_current_ectx(ectx_T *ectx);
int add_defer_function(char_u *name, int argcount, typval_T *argvars);
char_u *char_from_string(char_u *str, varnumber_T index);
char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, short loop_var_idx, short loop_var_count, ectx_T *ectx);
int may_load_script(int sid, int *loaded);
typval_T *lookup_debug_var(char_u *name);
int may_break_in_function(ufunc_T *ufunc);
+2 -2
View File
@@ -31,7 +31,7 @@ int generate_CHECKLEN(cctx_T *cctx, int min_len, int more_OK);
int generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name);
int generate_STORENR(cctx_T *cctx, int idx, varnumber_T value);
int generate_LOAD(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name, type_T *type);
int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, type_T *type);
int generate_LOADOUTER(cctx_T *cctx, int idx, int nesting, int loop_idx, type_T *type);
int generate_LOADV(cctx_T *cctx, char_u *name);
int generate_UNLET(cctx_T *cctx, isntype_T isn_type, char_u *name, int forceit);
int generate_LOCKCONST(cctx_T *cctx);
@@ -40,7 +40,7 @@ int generate_VIM9SCRIPT(cctx_T *cctx, isntype_T isn_type, int sid, int idx, type
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_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name, short loop_var_idx, short loop_var_count);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
int generate_WHILE(cctx_T *cctx, int funcref_idx);
+2 -2
View File
@@ -1817,14 +1817,14 @@ static regsubmatch_T rsm; // can only be used when can_f_submatch is TRUE
* call_func() by vim_regsub_both().
*/
static int
fill_submatch_list(int argc UNUSED, typval_T *argv, int argskip, int argcount)
fill_submatch_list(int argc UNUSED, typval_T *argv, int argskip, ufunc_T *fp)
{
listitem_T *li;
int i;
char_u *s;
typval_T *listarg = argv + argskip;
if (argcount == argskip)
if (!has_varargs(fp) && fp->uf_args.ga_len <= argskip)
// called function doesn't take a submatches argument
return argskip;
+15 -7
View File
@@ -1656,7 +1656,7 @@ typedef enum {
/*
* Structure to hold info for a user function.
* When adding a field check copy_func().
* When adding a field check copy_lambda_to_global_func().
*/
typedef struct
{
@@ -1741,7 +1741,8 @@ typedef struct
#define FC_NOARGS 0x200 // no a: variables in lambda
#define FC_VIM9 0x400 // defined in vim9 script file
#define FC_CFUNC 0x800 // defined as Lua C func
#define FC_COPY 0x1000 // copy of another function by copy_func()
#define FC_COPY 0x1000 // copy of another function by
// copy_lambda_to_global_func()
#define FC_LAMBDA 0x2000 // one line "return {expr}"
#define MAX_FUNC_ARGS 20 // maximum number of function arguments
@@ -2052,13 +2053,13 @@ typedef struct
// Struct passed between functions dealing with function call execution.
//
// "argv_func", when not NULL, can be used to fill in arguments only when the
// "fe_argv_func", when not NULL, can be used to fill in arguments only when the
// invoked function uses them. It is called like this:
// new_argcount = argv_func(current_argcount, argv, partial_argcount,
// called_func_argcount)
// new_argcount = fe_argv_func(current_argcount, argv, partial_argcount,
// called_func)
//
typedef struct {
int (* fe_argv_func)(int, typval_T *, int, int);
int (* fe_argv_func)(int, typval_T *, int, ufunc_T *);
linenr_T fe_firstline; // first line of range
linenr_T fe_lastline; // last line of range
int *fe_doesrange; // if not NULL: return: function handled range
@@ -2096,10 +2097,17 @@ struct funcstack_S
typedef struct outer_S outer_T;
struct outer_S {
garray_T *out_stack; // stack from outer scope
garray_T *out_stack; // stack from outer scope, or a copy
// containing only arguments and local vars
int out_frame_idx; // index of stack frame in out_stack
outer_T *out_up; // outer scope of outer scope or NULL
partial_T *out_up_partial; // partial owning out_up or NULL
garray_T *out_loop_stack; // stack from outer scope, or a copy
// containing only vars inside the loop
short out_loop_var_idx; // first variable defined in a loop
// in out_loop_stack
short out_loop_var_count; // number of variables defined in a loop
};
struct partial_S
@@ -0,0 +1,9 @@
|f+0&#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @42
|s+0&#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @41
|o+0&#ffffff0|n|e| |t|w>o| @52
|t|h|r|e@1| |f|o|u|r| @49
@3|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @43
|f+0&#ffffff0|i|v|e| |s|i|x| @51
|~+0#4040ff13&| @58
|~| @58
| +0#0000000&@41|1|,|7|-|1|2|7| @6|A|l@1|
@@ -0,0 +1,9 @@
|f+0&#ffff4012|i|r|s|t| |t|h|i|n|g| |a|b|o|v|e| @42
|s+0&#ffd7ff255|e|c|o|n|d| |t|h|i|n|g| |a|b|o|v|e| @41
>o+0&#ffffff0|n|e| |t|w|o| @52
|t|h|r|e@1| |f|o|u|r| @49
@3|a+0&#ffff4012|n|o|t|h|e|r| |t|h|i|n|g| @43
|f+0&#ffffff0|i|v|e| |s|i|x| @51
|~+0#4040ff13&| @58
|~| @58
| +0#0000000&@41|1|,|1|-|1|2|1| @6|A|l@1|
+3
View File
@@ -662,6 +662,9 @@ func Test_fullcommand()
\ '3match': 'match',
\ 'aboveleft': 'aboveleft',
\ 'abo': 'aboveleft',
\ 'en': 'endif',
\ 'end': 'endif',
\ 'endi': 'endif',
\ 's': 'substitute',
\ '5s': 'substitute',
\ ':5s': 'substitute',
+1
View File
@@ -595,6 +595,7 @@ let s:filename_checks = {
\ 'usw2kagtlog': ['usw2kagt.log', 'USW2KAGT.LOG', 'usw2kagt.file.log', 'USW2KAGT.FILE.LOG', 'file.usw2kagt.log', 'FILE.USW2KAGT.LOG'],
\ 'vala': ['file.vala'],
\ 'vb': ['file.sba', 'file.vb', 'file.vbs', 'file.dsm', 'file.ctl'],
\ 'vdf': ['file.vdf'],
\ 'vdmpp': ['file.vpp', 'file.vdmpp'],
\ 'vdmrt': ['file.vdmrt'],
\ 'vdmsl': ['file.vdm', 'file.vdmsl'],
+15 -7
View File
@@ -439,20 +439,28 @@ endfunc
func SubReplacer(text, submatches)
return a:text .. a:submatches[0] .. a:text
endfunc
func SubReplacerVar(text, ...)
return a:text .. a:1[0] .. a:text
endfunc
def SubReplacerVar9(text: string, ...args: list<list<string>>): string
return text .. args[0][0] .. text
enddef
func SubReplacer20(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15, t16, t17, t18, t19, submatches)
return a:t3 .. a:submatches[0] .. a:t11
endfunc
func Test_substitute_partial()
call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g'))
call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacer', ['foo']), 'g'))
call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar', ['foo']), 'g'))
call assert_equal('1foo2foo3', substitute('123', '2', function('SubReplacerVar9', ['foo']), 'g'))
" 19 arguments plus one is just OK
let Replacer = function('SubReplacer20', repeat(['foo'], 19))
call assert_equal('1foo2foo3', substitute('123', '2', Replacer, 'g'))
" 19 arguments plus one is just OK
let Replacer = function('SubReplacer20', repeat(['foo'], 19))
call assert_equal('1foo2foo3', substitute('123', '2', Replacer, 'g'))
" 20 arguments plus one is too many
let Replacer = function('SubReplacer20', repeat(['foo'], 20))
call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118:')
" 20 arguments plus one is too many
let Replacer = function('SubReplacer20', repeat(['foo'], 20))
call assert_fails("call substitute('123', '2', Replacer, 'g')", 'E118:')
endfunc
func Test_substitute_float()
+5
View File
@@ -2869,6 +2869,11 @@ func Test_props_with_text_above()
let buf = RunVimInTerminal('-S XscriptPropsWithTextAbove', #{rows: 9, cols: 60})
call VerifyScreenDump(buf, 'Test_prop_with_text_above_1', {})
call term_sendkeys(buf, "ggg$")
call VerifyScreenDump(buf, 'Test_prop_with_text_above_1a', {})
call term_sendkeys(buf, "g0")
call VerifyScreenDump(buf, 'Test_prop_with_text_above_1b', {})
call term_sendkeys(buf, "ggI")
call VerifyScreenDump(buf, 'Test_prop_with_text_above_2', {})
call term_sendkeys(buf, "inserted \<Esc>")
+7
View File
@@ -1530,6 +1530,13 @@ def Test_fullcommand()
assert_equal('scriptnames', fullcommand('scr'))
assert_equal('', fullcommand('scg'))
fullcommand('')->assert_equal('')
assert_equal('', fullcommand('en'))
legacy call assert_equal('endif', fullcommand('en'))
assert_equal('endif', fullcommand('en', 0))
legacy call assert_equal('endif', fullcommand('en', 0))
assert_equal('', fullcommand('en', 1))
legacy call assert_equal('', fullcommand('en', 1))
enddef
def Test_funcref()
+40 -62
View File
@@ -400,11 +400,10 @@ def Test_block_local_vars()
# need to execute this with a separate Vim instance to avoid the current
# context gets garbage collected.
writefile(lines, 'Xscript')
writefile(lines, 'Xscript', 'D')
g:RunVim([], [], '-S Xscript')
assert_equal(['ok'], readfile('Xdidit'))
delete('Xscript')
delete('Xdidit')
enddef
@@ -991,13 +990,12 @@ def Test_cnext_works_in_catch()
writefile([getqflist({idx: 0}).idx], 'Xcncresult')
qall
END
writefile(lines, 'XCatchCnext')
writefile(lines, 'XCatchCnext', 'D')
g:RunVim([], [], '--clean -S XCatchCnext')
assert_equal(['1'], readfile('Xcncresult'))
delete('Xcncfile1')
delete('Xcncfile2')
delete('XCatchCnext')
delete('Xcncresult')
enddef
@@ -1015,9 +1013,8 @@ def Test_nocatch_throw_silenced()
enddef
silent! Func()
END
writefile(lines, 'XthrowSilenced')
writefile(lines, 'XthrowSilenced', 'D')
source XthrowSilenced
delete('XthrowSilenced')
enddef
def DeletedFunc(): list<any>
@@ -1473,7 +1470,7 @@ def Test_vim9script_reload_delfunc()
END
# FuncNo() is defined
writefile(first_lines + withno_lines, 'Xreloaded.vim')
writefile(first_lines + withno_lines, 'Xreloaded.vim', 'D')
source Xreloaded.vim
g:DoCheck(true)
@@ -1486,8 +1483,6 @@ def Test_vim9script_reload_delfunc()
writefile(first_lines + withno_lines, 'Xreloaded.vim')
source Xreloaded.vim
g:DoCheck(false)
delete('Xreloaded.vim')
enddef
def Test_vim9script_reload_delvar()
@@ -1496,7 +1491,7 @@ def Test_vim9script_reload_delvar()
vim9script
var name = 'string'
END
writefile(lines, 'XreloadVar.vim')
writefile(lines, 'XreloadVar.vim', 'D')
source XreloadVar.vim
# now write the script using the same variable locally - works
@@ -1508,8 +1503,6 @@ def Test_vim9script_reload_delvar()
END
writefile(lines, 'XreloadVar.vim')
source XreloadVar.vim
delete('XreloadVar.vim')
enddef
def Test_func_redefine_error()
@@ -1520,7 +1513,7 @@ def Test_func_redefine_error()
'enddef',
'Func()',
]
writefile(lines, 'Xtestscript.vim')
writefile(lines, 'Xtestscript.vim', 'D')
for count in range(3)
try
@@ -1531,8 +1524,6 @@ def Test_func_redefine_error()
assert_match('function <SNR>\d\+_Func, line 1', v:throwpoint)
endtry
endfor
delete('Xtestscript.vim')
enddef
def Test_func_redefine_fails()
@@ -2035,9 +2026,8 @@ def Test_for_outside_of_function()
endfor
assert_equal(' loop 1 loop 2 loop 3', result)
END
writefile(lines, 'Xvim9for.vim')
writefile(lines, 'Xvim9for.vim', 'D')
source Xvim9for.vim
delete('Xvim9for.vim')
enddef
def Test_for_skipped_block()
@@ -2271,6 +2261,18 @@ def Test_for_loop_with_closure()
END
v9.CheckDefAndScriptSuccess(lines)
# also works when the loop variable is used only once halfway the loops
lines =<< trim END
var Clo: func
for i in range(5)
if i == 3
Clo = () => i
endif
endfor
assert_equal(4, Clo())
END
v9.CheckDefAndScriptSuccess(lines)
# using a local variable set to the loop variable in a closure results in the
# value at that moment
lines =<< trim END
@@ -3330,12 +3332,11 @@ def Test_finish()
finish
g:res = 'three'
END
writefile(lines, 'Xfinished')
writefile(lines, 'Xfinished', 'D')
source Xfinished
assert_equal('two', g:res)
unlet g:res
delete('Xfinished')
enddef
def Test_forward_declaration()
@@ -3349,14 +3350,13 @@ def Test_forward_declaration()
theVal = 'else'
g:laterVal = GetValue()
END
writefile(lines, 'Xforward')
writefile(lines, 'Xforward', 'D')
source Xforward
assert_equal('something', g:initVal)
assert_equal('else', g:laterVal)
unlet g:initVal
unlet g:laterVal
delete('Xforward')
enddef
def Test_declare_script_var_in_func()
@@ -3408,7 +3408,7 @@ func Test_vim9script_not_global()
echo 'local'
enddef
END
call writefile(vim9lines, 'Xvim9script.vim')
call writefile(vim9lines, 'Xvim9script.vim', 'D')
source Xvim9script.vim
try
echo g:var
@@ -3428,8 +3428,6 @@ func Test_vim9script_not_global()
catch /E117:/
" caught
endtry
call delete('Xvim9script.vim')
endfunc
def Test_vim9_copen()
@@ -3459,7 +3457,7 @@ def Test_error_in_autoload_script()
var save_rtp = &rtp
var dir = getcwd() .. '/Xruntime'
&rtp = dir
mkdir(dir .. '/autoload', 'p')
mkdir(dir .. '/autoload', 'pR')
var lines =<< trim END
vim9script noclear
@@ -3492,12 +3490,11 @@ def Test_error_in_autoload_script()
v9.CheckScriptSuccess(lines)
&rtp = save_rtp
delete(dir, 'rf')
enddef
def Test_error_in_autoload_script_foldexpr()
var save_rtp = &rtp
mkdir('Xvim/autoload', 'p')
mkdir('Xvim/autoload', 'pR')
&runtimepath = 'Xvim'
var lines =<< trim END
@@ -3515,8 +3512,6 @@ def Test_error_in_autoload_script_foldexpr()
redraw
END
v9.CheckScriptFailure(lines, 'E684: List index out of range: 0')
delete('Xvim', 'rf')
enddef
def Test_invalid_sid()
@@ -3529,16 +3524,14 @@ def Test_invalid_sid()
enddef
def Test_restoring_cpo()
writefile(['vim9script', 'set nocp'], 'Xsourced')
writefile(['call writefile(["done"], "Xdone")', 'quit!'], 'Xclose')
writefile(['vim9script', 'set nocp'], 'Xsourced', 'D')
writefile(['call writefile(["done"], "Xdone")', 'quit!'], 'Xclose', 'D')
if g:RunVim([], [], '-u NONE +"set cpo+=a" -S Xsourced -S Xclose')
assert_equal(['done'], readfile('Xdone'))
endif
delete('Xsourced')
delete('Xclose')
delete('Xdone')
writefile(['vim9script', 'g:cpoval = &cpo'], 'XanotherScript')
writefile(['vim9script', 'g:cpoval = &cpo'], 'XanotherScript', 'D')
set cpo=aABceFsMny>
edit XanotherScript
so %
@@ -3551,7 +3544,6 @@ def Test_restoring_cpo()
assert_equal('aABceFsMny>', &cpo)
assert_equal('aABceFsMny>', g:cpoval)
delete('XanotherScript')
set cpo&vim
unlet g:cpoval
@@ -3559,7 +3551,7 @@ def Test_restoring_cpo()
# 'cpo' is not restored in main vimrc
var save_HOME = $HOME
$HOME = getcwd() .. '/Xhome'
mkdir('Xhome')
mkdir('Xhome', 'R')
var lines =<< trim END
vim9script
writefile(['before: ' .. &cpo], 'Xrporesult')
@@ -3571,14 +3563,14 @@ def Test_restoring_cpo()
lines =<< trim END
call writefile(['later: ' .. &cpo], 'Xrporesult', 'a')
END
writefile(lines, 'Xlegacy')
writefile(lines, 'Xlegacy', 'D')
lines =<< trim END
vim9script
call writefile(['vim9: ' .. &cpo], 'Xrporesult', 'a')
qa
END
writefile(lines, 'Xvim9')
writefile(lines, 'Xvim9', 'D')
var cmd = g:GetVimCommand() .. " -S Xlegacy -S Xvim9"
cmd = substitute(cmd, '-u NONE', '', '')
@@ -3591,9 +3583,6 @@ def Test_restoring_cpo()
'vim9: aABceFs'], readfile('Xrporesult'))
$HOME = save_HOME
delete('Xhome', 'rf')
delete('Xlegacy')
delete('Xvim9')
delete('Xrporesult')
endif
enddef
@@ -3611,7 +3600,7 @@ def Run_test_no_redraw_when_restoring_cpo()
export def Func()
enddef
END
mkdir('Xnordir/autoload', 'p')
mkdir('Xnordir/autoload', 'pR')
writefile(lines, 'Xnordir/autoload/script.vim')
lines =<< trim END
@@ -3621,7 +3610,7 @@ def Run_test_no_redraw_when_restoring_cpo()
au CmdlineEnter : ++once timer_start(0, (_) => script#Func())
setline(1, 'some text')
END
writefile(lines, 'XTest_redraw_cpo')
writefile(lines, 'XTest_redraw_cpo', 'D')
var buf = g:RunVimInTerminal('-S XTest_redraw_cpo', {'rows': 6})
term_sendkeys(buf, "V:")
g:VerifyScreenDump(buf, 'Test_vim9_no_redraw', {})
@@ -3629,8 +3618,6 @@ def Run_test_no_redraw_when_restoring_cpo()
# clean up
term_sendkeys(buf, "\<Esc>u")
g:StopVimInTerminal(buf)
delete('XTest_redraw_cpo')
delete('Xnordir', 'rf')
enddef
func Test_reject_declaration()
@@ -3731,8 +3718,8 @@ def Run_Test_define_func_at_command_line()
call writefile(['errors: ' .. string(v:errors)], 'Xdidcmd')
endfunc
END
writefile([''], 'Xdidcmd')
writefile(lines, 'XcallFunc')
writefile([''], 'Xdidcmd', 'D')
writefile(lines, 'XcallFunc', 'D')
var buf = g:RunVimInTerminal('-S XcallFunc', {rows: 6})
# define Afunc() on the command line
term_sendkeys(buf, ":def Afunc()\<CR>Bfunc()\<CR>enddef\<CR>")
@@ -3740,8 +3727,6 @@ def Run_Test_define_func_at_command_line()
g:WaitForAssert(() => assert_equal(['errors: []'], readfile('Xdidcmd')))
call g:StopVimInTerminal(buf)
delete('XcallFunc')
delete('Xdidcmd')
enddef
def Test_script_var_scope()
@@ -3869,9 +3854,8 @@ def Test_no_unknown_error_after_error()
sleep 10m
endfor
END
writefile(lines, 'Xdef')
writefile(lines, 'Xdef', 'D')
assert_fails('so Xdef', ['E684:', 'E1012:'])
delete('Xdef')
enddef
def InvokeNormal()
@@ -3914,7 +3898,7 @@ def Test_script_var_gone_when_sourced_twice()
name = arg
enddef
END
writefile(lines, 'XscriptTwice.vim')
writefile(lines, 'XscriptTwice.vim', 'D')
so XscriptTwice.vim
assert_equal('thename', g:GetName())
g:SetName('newname')
@@ -3925,7 +3909,6 @@ def Test_script_var_gone_when_sourced_twice()
delfunc g:GetName
delfunc g:SetName
delete('XscriptTwice.vim')
unlet g:guard
enddef
@@ -4102,7 +4085,7 @@ def Run_Test_debug_with_lambda()
breakadd func Func
Func()
END
writefile(lines, 'XdebugFunc')
writefile(lines, 'XdebugFunc', 'D')
var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 6, wait_for_ruler: 0})
g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6)))
@@ -4110,7 +4093,6 @@ def Run_Test_debug_with_lambda()
g:WaitForAssert(() => assert_match('\[0\]', term_getline(buf, 5)))
g:StopVimInTerminal(buf)
delete('XdebugFunc')
enddef
func Test_debug_running_out_of_lines()
@@ -4138,7 +4120,7 @@ def Run_Test_debug_running_out_of_lines()
breakadd func Crash
Crash()
END
writefile(lines, 'XdebugFunc')
writefile(lines, 'XdebugFunc', 'D')
var buf = g:RunVimInTerminal('-S XdebugFunc', {rows: 6, wait_for_ruler: 0})
g:WaitForAssert(() => assert_match('^>', term_getline(buf, 6)))
@@ -4150,7 +4132,6 @@ def Run_Test_debug_running_out_of_lines()
g:TermWait(buf)
g:StopVimInTerminal(buf)
delete('XdebugFunc')
enddef
def Test_ambigous_command_error()
@@ -4239,7 +4220,7 @@ def Test_profile_with_lambda()
writefile([result], 'Xdidprofile')
endtry
END
writefile(lines, 'Xprofile.vim')
writefile(lines, 'Xprofile.vim', 'D')
call system(g:GetVimCommand()
.. ' --clean'
.. ' -c "so Xprofile.vim"'
@@ -4250,7 +4231,6 @@ def Test_profile_with_lambda()
assert_true(filereadable('Xprofile.log'))
delete('Xdidprofile')
delete('Xprofile.log')
delete('Xprofile.vim')
enddef
func Test_misplaced_type()
@@ -4323,10 +4303,8 @@ def Test_substitute_cmd()
assert_equal('otherthing', getline(1))
bwipe!
END
writefile(lines, 'Xvim9lines')
writefile(lines, 'Xvim9lines', 'D')
source Xvim9lines
delete('Xvim9lines')
enddef
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
+14
View File
@@ -572,4 +572,18 @@ func Test_virtualedit_mouse()
set virtualedit&
endfunc
" this was replacing the NUL at the end of the line
func Test_virtualedit_replace_after_tab()
new
s/\v/ 0
set ve=all
let @" = ''
sil! norm vPvr0
call assert_equal("\t0", getline(1))
set ve&
bwipe!
endfunc
" vim: shiftwidth=2 sts=2 expandtab
+100 -107
View File
@@ -1637,127 +1637,119 @@ endfunc
func Test_splitscroll_with_splits()
set nowrap
set nosplitscroll
" disallow window resizing
let save_WS = &t_WS
set t_WS=
let gui = has("gui_running")
inoremap c <cmd>:copen<CR>
for winbar in [0, 1]
for sb in [0, 1]
for ea in [0, 1]
for tab in [0, 1]
for so in [0, 5]
for ls in range(0, 2)
for pos in ["H", "M", "L"]
tabnew | tabonly! | redraw
let tabline = (gui ? 0 : (tab ? 1 : 0))
let winbar_sb = (sb ? winbar : 0)
execute 'set scrolloff=' . so
execute 'set laststatus=' . ls
execute 'set ' . (ea ? 'equalalways' : 'noequalalways')
execute 'set ' . (sb ? 'splitbelow' : 'nosplitbelow')
execute tab ? 'tabnew' : ''
execute winbar ? 'nnoremenu 1.10 WinBar.Test :echo' : ''
call setline(1, range(1, 256))
" No scroll for restore_snapshot
norm G
try
copen | close | colder
catch /E380/
endtry
call assert_equal(257 - winheight(0), line("w0"))
inoremap <expr> c "<cmd>copen<bar>wincmd k<CR>"
for run in range(0, 10)
tabnew | tabonly! | redraw
let tabline = (gui ? 0 : ((run % 5) ? 1 : 0))
let winbar_sb = (run % 2) && (run % 3)
execute 'set scrolloff=' . !(run % 3) ? 0 : run
execute 'set laststatus=' . (run % 3)
execute 'set ' . ((run % 2) ? 'equalalways' : 'noequalalways')
execute 'set ' . ((run % 3) ? 'splitbelow' : 'nosplitbelow')
execute (run % 5) ? 'tabnew' : ''
execute (run % 2) ? 'nnoremenu 1.10 WinBar.Test :echo' : ''
let pos = !(run % 3) ? 'H' : ((run % 2) ? 'M' : 'L')
call setline(1, range(1, 256))
" No scroll for restore_snapshot
norm G
try
copen | close | colder
catch /E380/
endtry
call assert_equal(257 - winheight(0), line("w0"))
" No scroll for firstwin horizontal split
execute 'norm gg' . pos
split | redraw | wincmd k
call assert_equal(1, line("w0"))
call assert_equal(&scroll, winheight(0) / 2)
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll for firstwin horizontal split
execute 'norm gg' . pos
split | redraw | wincmd k
call assert_equal(1, line("w0"))
call assert_equal(&scroll, winheight(0) / 2)
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll when resizing windows
wincmd k | resize +2
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll when resizing windows
wincmd k | resize +2
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll when dragging statusline
call win_move_statusline(1, -3)
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd k
call assert_equal(1, line("w0"))
" No scroll when dragging statusline
call win_move_statusline(1, -3)
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd k
call assert_equal(1, line("w0"))
" No scroll when changing shellsize
set lines+=2
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
set lines-=2
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd k
call assert_equal(1, line("w0"))
" No scroll when changing shellsize
set lines+=2
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
set lines-=2
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd k
call assert_equal(1, line("w0"))
" No scroll when equalizing windows
wincmd =
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd k
call assert_equal(1, line("w0"))
" No scroll when equalizing windows
wincmd =
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd k
call assert_equal(1, line("w0"))
" No scroll in windows split multiple times
vsplit | split | 4wincmd w
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
1wincmd w | quit | wincmd l | split
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll in windows split multiple times
vsplit | split | 4wincmd w
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
1wincmd w | quit | wincmd l | split
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll in small window
2wincmd w | only | 5split | wincmd k
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll in small window
2wincmd w | only | 5split | wincmd k
call assert_equal(1, line("w0"))
wincmd j
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll for vertical split
quit | vsplit | wincmd l
call assert_equal(1, line("w0"))
wincmd h
call assert_equal(1, line("w0"))
" No scroll for vertical split
quit | vsplit | wincmd l
call assert_equal(1, line("w0"))
wincmd h
call assert_equal(1, line("w0"))
" No scroll in windows split and quit multiple times
quit | redraw | split | redraw | split | redraw | quit | redraw
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll in windows split and quit multiple times
quit | redraw | split | redraw | split | redraw | quit | redraw
call assert_equal(win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
" No scroll for new buffer
1wincmd w | only | copen | wincmd k
call assert_equal(1, line("w0"))
only
call assert_equal(1, line("w0"))
above copen | wincmd j
call assert_equal(win_screenpos(0)[0] - tabline, line("w0"))
" No scroll for new buffer
1wincmd w | only | copen | wincmd k
call assert_equal(1, line("w0"))
only
call assert_equal(1, line("w0"))
above copen | wincmd j
call assert_equal(win_screenpos(0)[0] - tabline, line("w0"))
" No scroll when opening cmdwin, and no cursor move when closing
" cmdwin.
only | norm ggL
let curpos = getcurpos()
norm q:
call assert_equal(1, line("w0"))
call assert_equal(curpos, getcurpos())
" No scroll when opening cmdwin, and no cursor move when closing cmdwin.
only | norm ggL
let curpos = getcurpos()
norm q:
call assert_equal(1, line("w0"))
call assert_equal(curpos, getcurpos())
" Scroll when cursor becomes invalid in insert mode
norm Lic
wincmd k | only
call assert_notequal(1, line("w0"))
" Scroll when cursor becomes invalid in insert mode
norm Lic
call assert_equal(curpos, getcurpos())
" No scroll when topline not equal to 1
execute "norm gg5\<C-e>" | split | wincmd k
call assert_equal(6, line("w0"))
wincmd j
call assert_equal(5 + win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
endfor
endfor
endfor
endfor
endfor
endfor
" No scroll when topline not equal to 1
only | execute "norm gg5\<C-e>" | split | wincmd k
call assert_equal(6, line("w0"))
wincmd j
call assert_equal(5 + win_screenpos(0)[0] - tabline - winbar_sb, line("w0"))
endfor
tabnew | tabonly! | %bwipeout!
@@ -1768,6 +1760,7 @@ func Test_splitscroll_with_splits()
set laststatus&
set equalalways&
set splitscroll&
let &t_WS = save_WS
endfunc
function Test_nosplitscroll_cmdwin_cursor_position()
+9 -3
View File
@@ -2452,7 +2452,12 @@ func_clear_free(ufunc_T *fp, int force)
* This is for when a compiled function defines a global function.
*/
int
copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
copy_lambda_to_global_func(
char_u *lambda,
char_u *global,
short loop_var_idx,
short loop_var_count,
ectx_T *ectx)
{
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
ufunc_T *fp = NULL;
@@ -2519,7 +2524,8 @@ copy_func(char_u *lambda, char_u *global, ectx_T *ectx)
if (pt == NULL)
goto failed;
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
if (fill_partial_and_closure(pt, ufunc, loop_var_idx, loop_var_count,
ectx) == FAIL)
{
vim_free(pt);
goto failed;
@@ -3644,7 +3650,7 @@ call_func(
if (funcexe->fe_argv_func != NULL)
// postponed filling in the arguments, do it now
argcount = funcexe->fe_argv_func(argcount, argvars,
argv_clear, fp->uf_args.ga_len);
argv_clear, fp);
if (funcexe->fe_basetv != NULL)
{
+22
View File
@@ -718,6 +718,28 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
483,
/**/
482,
/**/
481,
/**/
480,
/**/
479,
/**/
478,
/**/
477,
/**/
476,
/**/
475,
/**/
474,
/**/
473,
/**/
472,
/**/
+20 -4
View File
@@ -354,16 +354,29 @@ typedef struct {
int ul_forceit; // forceit flag
} unlet_T;
// extra arguments for funcref_T
typedef struct {
char_u *fre_func_name; // function name for legacy function
short fre_loop_var_idx; // index of first variable inside loop
short fre_loop_var_count; // number of variables inside loop
} funcref_extra_T;
// arguments to ISN_FUNCREF
typedef struct {
int fr_dfunc_idx; // function index for :def function
char_u *fr_func_name; // function name for legacy function
int fr_dfunc_idx; // function index for :def function
funcref_extra_T *fr_extra; // optional extra information
} funcref_T;
// arguments to ISN_NEWFUNC
typedef struct {
char_u *nf_lambda; // name of the lambda already defined
char_u *nf_global; // name of the global function to be created
char_u *nfa_lambda; // name of the lambda already defined
char_u *nfa_global; // name of the global function to be created
short nfa_loop_var_idx; // index of first variable inside loop
short nfa_loop_var_count; // number of variables inside loop
} newfuncarg_T;
typedef struct {
newfuncarg_T *nf_arg;
} newfunc_T;
// arguments to ISN_CHECKLEN
@@ -401,6 +414,8 @@ typedef struct {
int outer_depth; // nesting level, stack frames to go up
} isn_outer_T;
#define OUTER_LOOP_DEPTH -9 // used for outer_depth for loop variables
// arguments to ISN_SUBSTITUTE
typedef struct {
char_u *subs_cmd; // :s command
@@ -677,6 +692,7 @@ typedef struct {
char_u *lv_name;
type_T *lv_type;
int lv_idx; // index of the variable on the stack
int lv_loop_idx; // index of first variable inside a loop or -1
int lv_from_outer; // nesting level, using ctx_outer scope
int lv_const; // when TRUE cannot be assigned to
int lv_arg; // when TRUE this is an argument
+43
View File
@@ -1245,6 +1245,49 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
return arg;
}
/*
* Get the current information about variables declared inside a loop.
* Returns zero if there are none, otherwise the count.
* "loop_var_idx" is then set to the index of the first variable.
*/
short
get_loop_var_info(cctx_T *cctx, short *loop_var_idx)
{
scope_T *scope = cctx->ctx_scope;
int start_local_count;
while (scope != NULL && scope->se_type != WHILE_SCOPE
&& scope->se_type != FOR_SCOPE)
scope = scope->se_outer;
if (scope == NULL)
return 0;
if (scope->se_type == WHILE_SCOPE)
start_local_count = scope->se_u.se_while.ws_local_count;
else
start_local_count = scope->se_u.se_for.fs_local_count;
if (cctx->ctx_locals.ga_len > start_local_count)
{
*loop_var_idx = (short)start_local_count;
return (short)(cctx->ctx_locals.ga_len - start_local_count);
}
return 0;
}
/*
* Get the index of the first variable in a loop, if any.
* Returns -1 if none.
*/
int
get_loop_var_idx(cctx_T *cctx)
{
short loop_var_idx;
if (get_loop_var_info(cctx, &loop_var_idx) > 0)
return loop_var_idx;
return -1;
}
/*
* compile "continue"
*/
+4 -2
View File
@@ -54,6 +54,7 @@ lookup_local(char_u *name, size_t len, lvar_T *lvar, cctx_T *cctx)
{
*lvar = *lvp;
lvar->lv_from_outer = 0;
lvar->lv_loop_idx = get_loop_var_idx(cctx);
}
return OK;
}
@@ -954,7 +955,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx, garray_T *lines_to_free)
// recursive call.
if (is_global)
{
r = generate_NEWFUNC(cctx, lambda_name, func_name);
// TODO: loop variable index and count
r = generate_NEWFUNC(cctx, lambda_name, func_name, 0, 0);
func_name = NULL;
lambda_name = NULL;
}
@@ -1193,7 +1195,7 @@ generate_loadvar(
{
if (lvar->lv_from_outer > 0)
generate_LOADOUTER(cctx, lvar->lv_idx, lvar->lv_from_outer,
type);
lvar->lv_loop_idx, type);
else
generate_LOAD(cctx, ISN_LOAD, lvar->lv_idx, NULL, type);
}
+93 -34
View File
@@ -673,6 +673,9 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
if (closure_count == 0)
return OK; // no funcrefs created
// Compute "top": the first entry in the stack used by the function.
// This is the first argument (after that comes the stack frame and then
// the local variables).
argcount = ufunc_argcount(dfunc->df_ufunc);
top = ectx->ec_frame_idx - argcount;
@@ -740,6 +743,7 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
else
copy_tv(tv, stack + idx);
}
// Skip the stack frame.
// Move the local variables.
for (idx = 0; idx < dfunc->df_varcount; ++idx)
{
@@ -770,10 +774,17 @@ handle_closure_in_use(ectx_T *ectx, int free_arguments)
- closure_count + idx];
if (pt->pt_refcount > 1)
{
int prev_frame_idx = pt->pt_outer.out_frame_idx;
++funcstack->fs_refcount;
pt->pt_funcstack = funcstack;
pt->pt_outer.out_stack = &funcstack->fs_ga;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx - top;
// TODO: drop this, should be done at ISN_ENDLOOP
pt->pt_outer.out_loop_stack = &funcstack->fs_ga;
pt->pt_outer.out_loop_var_idx -=
prev_frame_idx - pt->pt_outer.out_frame_idx;
}
}
}
@@ -1814,7 +1825,12 @@ call_eval_func(
* needed, especially when it is used as a closure.
*/
int
fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
fill_partial_and_closure(
partial_T *pt,
ufunc_T *ufunc,
short loop_var_idx,
short loop_var_count,
ectx_T *ectx)
{
pt->pt_func = ufunc;
pt->pt_refcount = 1;
@@ -1839,6 +1855,14 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
}
}
// The closure may need to find variables defined inside a loop. A
// new reference is made every time, ISN_ENDLOOP will check if they
// are actually used.
pt->pt_outer.out_loop_stack = &ectx->ec_stack;
pt->pt_outer.out_loop_var_idx = ectx->ec_frame_idx + STACK_FRAME_SIZE
+ loop_var_idx;
pt->pt_outer.out_loop_var_count = loop_var_count;
// If the function currently executing returns and the closure is still
// being referenced, we need to make a copy of the context (arguments
// and local variables) so that the closure can use it later.
@@ -1853,8 +1877,8 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
++(((typval_T *)ectx->ec_stack.ga_data) + ectx->ec_frame_idx
+ STACK_FRAME_SIZE + dfunc->df_varcount)->vval.v_number;
((partial_T **)ectx->ec_funcrefs.ga_data)
[ectx->ec_funcrefs.ga_len] = pt;
((partial_T **)ectx->ec_funcrefs.ga_data)[ectx->ec_funcrefs.ga_len]
= pt;
++pt->pt_refcount;
++ectx->ec_funcrefs.ga_len;
}
@@ -3610,9 +3634,15 @@ exec_instructions(ectx_T *ectx)
iemsg("LOADOUTER depth more than scope levels");
goto theend;
}
tv = ((typval_T *)outer->out_stack->ga_data)
+ outer->out_frame_idx + STACK_FRAME_SIZE
+ iptr->isn_arg.outer.outer_idx;
if (depth == OUTER_LOOP_DEPTH)
// variable declared in loop
tv = ((typval_T *)outer->out_loop_stack->ga_data)
+ outer->out_loop_var_idx
+ iptr->isn_arg.outer.outer_idx;
else
tv = ((typval_T *)outer->out_stack->ga_data)
+ outer->out_frame_idx + STACK_FRAME_SIZE
+ iptr->isn_arg.outer.outer_idx;
if (iptr->isn_type == ISN_LOADOUTER)
{
if (GA_GROW_FAILS(&ectx->ec_stack, 1))
@@ -3913,9 +3943,10 @@ exec_instructions(ectx_T *ectx)
// push a partial, a reference to a compiled function
case ISN_FUNCREF:
{
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
ufunc_T *ufunc;
funcref_T *funcref = &iptr->isn_arg.funcref;
partial_T *pt = ALLOC_CLEAR_ONE(partial_T);
ufunc_T *ufunc;
funcref_T *funcref = &iptr->isn_arg.funcref;
funcref_extra_T *extra = funcref->fr_extra;
if (pt == NULL)
goto theend;
@@ -3924,7 +3955,7 @@ exec_instructions(ectx_T *ectx)
vim_free(pt);
goto theend;
}
if (funcref->fr_func_name == NULL)
if (extra == NULL || extra->fre_func_name == NULL)
{
dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx;
@@ -3932,16 +3963,17 @@ exec_instructions(ectx_T *ectx)
ufunc = pt_dfunc->df_ufunc;
}
else
{
ufunc = find_func(funcref->fr_func_name, FALSE);
}
ufunc = find_func(extra->fre_func_name, FALSE);
if (ufunc == NULL)
{
SOURCING_LNUM = iptr->isn_lnum;
iemsg("ufunc unexpectedly NULL for FUNCREF");
goto theend;
}
if (fill_partial_and_closure(pt, ufunc, ectx) == FAIL)
if (fill_partial_and_closure(pt, ufunc,
extra == NULL ? 0 : extra->fre_loop_var_idx,
extra == NULL ? 0 : extra->fre_loop_var_count,
ectx) == FAIL)
goto theend;
tv = STACK_TV_BOT(0);
++ectx->ec_stack.ga_len;
@@ -3954,10 +3986,11 @@ exec_instructions(ectx_T *ectx)
// Create a global function from a lambda.
case ISN_NEWFUNC:
{
newfunc_T *newfunc = &iptr->isn_arg.newfunc;
newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg;
if (copy_func(newfunc->nf_lambda, newfunc->nf_global,
ectx) == FAIL)
if (copy_lambda_to_global_func(arg->nfa_lambda,
arg->nfa_global, arg->nfa_loop_var_idx,
arg->nfa_loop_var_count, ectx) == FAIL)
goto theend;
}
break;
@@ -5520,7 +5553,7 @@ call_def_function(
ufunc_T *base_ufunc = dfunc->df_ufunc;
// "uf_partial" is on the ufunc that "df_ufunc" points to, as is done
// by copy_func().
// by copy_lambda_to_global_func().
if (partial != NULL || base_ufunc->uf_partial != NULL)
{
ectx.ec_outer_ref = ALLOC_CLEAR_ONE(outer_ref_T);
@@ -5880,15 +5913,20 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
break;
case ISN_LOADOUTER:
{
if (iptr->isn_arg.outer.outer_idx < 0)
isn_outer_T *outer = &iptr->isn_arg.outer;
if (outer->outer_idx < 0)
smsg("%s%4d LOADOUTER level %d arg[%d]", pfx, current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx
outer->outer_depth,
outer->outer_idx
+ STACK_FRAME_SIZE);
else if (outer->outer_depth == OUTER_LOOP_DEPTH)
smsg("%s%4d LOADOUTER level 1 $%d in loop",
pfx, current, outer->outer_idx);
else
smsg("%s%4d LOADOUTER level %d $%d", pfx, current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx);
outer->outer_depth,
outer->outer_idx);
}
break;
case ISN_LOADV:
@@ -5971,9 +6009,16 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
iptr->isn_arg.number);
break;
case ISN_STOREOUTER:
smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
iptr->isn_arg.outer.outer_depth,
iptr->isn_arg.outer.outer_idx);
{
isn_outer_T *outer = &iptr->isn_arg.outer;
if (outer->outer_depth == OUTER_LOOP_DEPTH)
smsg("%s%4d STOREOUTER level 1 $%d in loop",
pfx, current, outer->outer_idx);
else
smsg("%s%4d STOREOUTER level %d $%d", pfx, current,
outer->outer_depth, outer->outer_idx);
}
break;
case ISN_STOREV:
smsg("%s%4d STOREV v:%s", pfx, current,
@@ -6190,27 +6235,41 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
break;
case ISN_FUNCREF:
{
funcref_T *funcref = &iptr->isn_arg.funcref;
char_u *name;
funcref_T *funcref = &iptr->isn_arg.funcref;
funcref_extra_T *extra = funcref->fr_extra;
char_u *name;
if (funcref->fr_func_name == NULL)
if (extra == NULL || extra->fre_func_name == NULL)
{
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
+ funcref->fr_dfunc_idx;
name = df->df_ufunc->uf_name;
}
else
name = funcref->fr_func_name;
smsg("%s%4d FUNCREF %s", pfx, current, name);
name = extra->fre_func_name;
if (extra == NULL || extra->fre_loop_var_count == 0)
smsg("%s%4d FUNCREF %s", pfx, current, name);
else
smsg("%s%4d FUNCREF %s var $%d - $%d", pfx, current,
name,
extra->fre_loop_var_idx,
extra->fre_loop_var_idx
+ extra->fre_loop_var_count - 1);
}
break;
case ISN_NEWFUNC:
{
newfunc_T *newfunc = &iptr->isn_arg.newfunc;
newfuncarg_T *arg = iptr->isn_arg.newfunc.nf_arg;
smsg("%s%4d NEWFUNC %s %s", pfx, current,
newfunc->nf_lambda, newfunc->nf_global);
if (arg->nfa_loop_var_count == 0)
smsg("%s%4d NEWFUNC %s %s", pfx, current,
arg->nfa_lambda, arg->nfa_global);
else
smsg("%s%4d NEWFUNC %s %s var $%d - $%d", pfx, current,
arg->nfa_lambda, arg->nfa_global,
arg->nfa_loop_var_idx,
arg->nfa_loop_var_idx + arg->nfa_loop_var_count - 1);
}
break;
+4 -1
View File
@@ -496,6 +496,7 @@ compile_load(
int idx;
int gen_load = FALSE;
int gen_load_outer = 0;
int outer_loop_idx = -1;
name = vim_strnsave(*arg, end - *arg);
if (name == NULL)
@@ -520,6 +521,7 @@ compile_load(
{
type = lvar.lv_type;
idx = lvar.lv_idx;
outer_loop_idx = lvar.lv_loop_idx;
if (lvar.lv_from_outer != 0)
gen_load_outer = lvar.lv_from_outer;
else
@@ -544,7 +546,8 @@ compile_load(
res = generate_LOAD(cctx, ISN_LOAD, idx, NULL, type);
if (gen_load_outer > 0)
{
res = generate_LOADOUTER(cctx, idx, gen_load_outer, type);
res = generate_LOADOUTER(cctx, idx,
gen_load_outer, outer_loop_idx, type);
cctx->ctx_outer_used = TRUE;
}
}
+94 -27
View File
@@ -916,15 +916,25 @@ generate_STORE(cctx_T *cctx, isntype_T isn_type, int idx, char_u *name)
* Generate an ISN_STOREOUTER instruction.
*/
static int
generate_STOREOUTER(cctx_T *cctx, int idx, int level)
generate_STOREOUTER(cctx_T *cctx, int idx, int level, int loop_idx)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_drop(cctx, ISN_STOREOUTER, 1)) == NULL)
return FAIL;
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = level;
if (level == 1 && loop_idx >= 0 && idx >= loop_idx)
{
// Store a variable defined in a loop. A copy will be made at the end
// of the loop. TODO: how about deeper nesting?
isn->isn_arg.outer.outer_idx = idx - loop_idx;
isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
}
else
{
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = level;
}
return OK;
}
@@ -999,6 +1009,7 @@ generate_LOADOUTER(
cctx_T *cctx,
int idx,
int nesting,
int loop_idx,
type_T *type)
{
isn_T *isn;
@@ -1006,8 +1017,18 @@ generate_LOADOUTER(
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr_type2(cctx, ISN_LOADOUTER, type, type)) == NULL)
return FAIL;
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = nesting;
if (nesting == 1 && loop_idx >= 0 && idx >= loop_idx)
{
// Load a variable defined in a loop. A copy will be made at the end
// of the loop. TODO: how about deeper nesting?
isn->isn_arg.outer.outer_idx = idx - loop_idx;
isn->isn_arg.outer.outer_depth = OUTER_LOOP_DEPTH;
}
else
{
isn->isn_arg.outer.outer_idx = idx;
isn->isn_arg.outer.outer_depth = nesting;
}
return OK;
}
@@ -1186,20 +1207,39 @@ generate_NEWDICT(cctx_T *cctx, int count, int use_null)
/*
* Generate an ISN_FUNCREF instruction.
* "isnp" is set to the instruction, so that fr_dfunc_idx can be set later.
* If variables were declared inside a loop "loop_var_idx" is the index of the
* first one and "loop_var_count" the number of variables declared.
*/
int
generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
generate_FUNCREF(
cctx_T *cctx,
ufunc_T *ufunc,
isn_T **isnp)
{
isn_T *isn;
type_T *type;
isn_T *isn;
type_T *type;
funcref_extra_T *extra;
short loop_var_idx;
short loop_var_count;
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL;
if (isnp != NULL)
*isnp = isn;
loop_var_count = get_loop_var_info(cctx, &loop_var_idx);
if (ufunc->uf_def_status == UF_NOT_COMPILED || loop_var_count > 0)
{
extra = ALLOC_CLEAR_ONE(funcref_extra_T);
if (extra == NULL)
return FAIL;
isn->isn_arg.funcref.fr_extra = extra;
extra->fre_loop_var_idx = loop_var_idx;
extra->fre_loop_var_count = loop_var_count;
}
if (ufunc->uf_def_status == UF_NOT_COMPILED)
isn->isn_arg.funcref.fr_func_name = vim_strsave(ufunc->uf_name);
extra->fre_func_name = vim_strsave(ufunc->uf_name);
else
isn->isn_arg.funcref.fr_dfunc_idx = ufunc->uf_dfunc_idx;
cctx->ctx_has_closure = 1;
@@ -1221,7 +1261,12 @@ generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp)
* consumed.
*/
int
generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
generate_NEWFUNC(
cctx_T *cctx,
char_u *lambda_name,
char_u *func_name,
short loop_var_idx,
short loop_var_count)
{
isn_T *isn;
int ret = OK;
@@ -1232,9 +1277,19 @@ generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name)
ret = FAIL;
else
{
isn->isn_arg.newfunc.nf_lambda = lambda_name;
isn->isn_arg.newfunc.nf_global = func_name;
return OK;
newfuncarg_T *arg = ALLOC_CLEAR_ONE(newfuncarg_T);
if (arg == NULL)
ret = FAIL;
else
{
isn->isn_arg.newfunc.nf_arg = arg;
arg->nfa_lambda = lambda_name;
arg->nfa_global = func_name;
arg->nfa_loop_var_idx = loop_var_idx;
arg->nfa_loop_var_count = loop_var_count;
return OK;
}
}
}
vim_free(lambda_name);
@@ -2123,7 +2178,7 @@ generate_store_lhs(cctx_T *cctx, lhs_T *lhs, int instr_count, int is_decl)
}
else if (lhs->lhs_lvar->lv_from_outer > 0)
generate_STOREOUTER(cctx, lhs->lhs_lvar->lv_idx,
lhs->lhs_lvar->lv_from_outer);
lhs->lhs_lvar->lv_from_outer, lhs->lhs_lvar->lv_loop_idx);
else
generate_STORE(cctx, ISN_STORE, lhs->lhs_lvar->lv_idx, NULL);
}
@@ -2226,22 +2281,28 @@ delete_instr(isn_T *isn)
case ISN_FUNCREF:
{
if (isn->isn_arg.funcref.fr_func_name == NULL)
funcref_T *funcref = &isn->isn_arg.funcref;
funcref_extra_T *extra = funcref->fr_extra;
if (extra == NULL || extra->fre_func_name == NULL)
{
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ isn->isn_arg.funcref.fr_dfunc_idx;
+ funcref->fr_dfunc_idx;
ufunc_T *ufunc = dfunc->df_ufunc;
if (ufunc != NULL && func_name_refcount(ufunc->uf_name))
func_ptr_unref(ufunc);
}
else
if (extra != NULL)
{
char_u *name = isn->isn_arg.funcref.fr_func_name;
char_u *name = extra->fre_func_name;
if (name != NULL)
{
func_unref(name);
vim_free(isn->isn_arg.funcref.fr_func_name);
vim_free(name);
}
vim_free(extra);
}
}
break;
@@ -2259,17 +2320,23 @@ delete_instr(isn_T *isn)
case ISN_NEWFUNC:
{
char_u *lambda = isn->isn_arg.newfunc.nf_lambda;
ufunc_T *ufunc = find_func_even_dead(lambda, FFED_IS_GLOBAL);
newfuncarg_T *arg = isn->isn_arg.newfunc.nf_arg;
if (ufunc != NULL)
if (arg != NULL)
{
unlink_def_function(ufunc);
func_ptr_unref(ufunc);
}
ufunc_T *ufunc = find_func_even_dead(
arg->nfa_lambda, FFED_IS_GLOBAL);
vim_free(lambda);
vim_free(isn->isn_arg.newfunc.nf_global);
if (ufunc != NULL)
{
unlink_def_function(ufunc);
func_ptr_unref(ufunc);
}
vim_free(arg->nfa_lambda);
vim_free(arg->nfa_global);
vim_free(arg);
}
}
break;
+5
View File
@@ -20,6 +20,11 @@
# include <float.h>
#endif
// When not generating protos this is included in proto.h
#ifdef PROTO
# include "vim9.h"
#endif
/*
* Allocate memory for a type_T and add the pointer to type_gap, so that it can
* be easily freed later.
+6 -3
View File
@@ -6409,7 +6409,6 @@ win_fix_scroll(int resize)
static void
win_fix_cursor(int normal)
{
int top = FALSE;
win_T *wp = curwin;
long so = get_scrolloff_value();
linenr_T nlnum = 0;
@@ -6424,7 +6423,7 @@ win_fix_cursor(int normal)
so = MIN(wp->w_height / 2, so);
// Check if cursor position is above topline or below botline.
if (wp->w_cursor.lnum < (wp->w_topline + so) && wp->w_topline != 1)
top = nlnum = MIN(wp->w_topline + so, wp->w_buffer->b_ml.ml_line_count);
nlnum = MIN(wp->w_topline + so, wp->w_buffer->b_ml.ml_line_count);
else if (wp->w_cursor.lnum > (wp->w_botline - so - 1)
&& (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1)
nlnum = MAX(wp->w_botline - so - 1, 1);
@@ -6442,7 +6441,11 @@ win_fix_cursor(int normal)
}
else
{ // Ensure cursor stays visible if we are not in normal mode.
wp->w_fraction = top ? 0 : FRACTION_MULT;
wp->w_fraction = 0.5 * FRACTION_MULT;
// Make sure cursor is closer to topline than botline.
if (so == wp->w_height / 2
&& nlnum - wp->w_topline > wp->w_botline - 1 - nlnum)
wp->w_fraction++;
scroll_to_fraction(wp, wp->w_prev_height);
}
}