diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index fb8b116010..53179ca85a 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -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". diff --git a/runtime/filetype.vim b/runtime/filetype.vim index a27c3c71cb..aac17aad09 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -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 diff --git a/runtime/optwin.vim b/runtime/optwin.vim index a181a5afb0..d73d57475a 100644 --- a/runtime/optwin.vim +++ b/runtime/optwin.vim @@ -515,7 +515,7 @@ call AddOption("splitbelow", gettext("a new window is put below the current call BinOptionG("sb", &sb) call AddOption("splitright", gettext("a new window is put right of the current one")) call BinOptionG("spr", &spr) -call AddOption("splitscroll", gettext("determines scroll behavior when spliting windows")) +call AddOption("splitscroll", gettext("determines scroll behavior for split windows")) call BinOptionG("spsc", &spsc) call AddOption("scrollbind", gettext("this window scrolls together with other bound windows")) call append("$", "\t" .. s:local_to_window) diff --git a/src/Makefile b/src/Makefile index fcaa36da00..4096164401 100644 --- a/src/Makefile +++ b/src/Makefile @@ -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 \ diff --git a/src/evalfunc.c b/src/evalfunc.c index d0aa36cb9c..ead67afa0f 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -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}, diff --git a/src/ex_docmd.c b/src/ex_docmd.c index 5eb7905863..c3d8ca9047 100644 --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -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 diff --git a/src/move.c b/src/move.c index 4b823ba574..2ef7a9ce36 100644 --- a/src/move.c +++ b/src/move.c @@ -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(); } /* diff --git a/src/normal.c b/src/normal.c index 121d83d010..e3a903f08f 100644 --- a/src/normal.c +++ b/src/normal.c @@ -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(); } } diff --git a/src/ops.c b/src/ops.c index 88d85f8e0b..99a5d5cdfd 100644 --- a/src/ops.c +++ b/src/ops.c @@ -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; diff --git a/src/proto/move.pro b/src/proto/move.pro index f9787d6a6f..de8bf51e12 100644 --- a/src/proto/move.pro +++ b/src/proto/move.pro @@ -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); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro index e5543f389d..3cc35762f0 100644 --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -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); diff --git a/src/proto/vim9cmds.pro b/src/proto/vim9cmds.pro index 79a155780e..a0c3adec55 100644 --- a/src/proto/vim9cmds.pro +++ b/src/proto/vim9cmds.pro @@ -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); diff --git a/src/proto/vim9execute.pro b/src/proto/vim9execute.pro index b8360c5e65..e624f5581e 100644 --- a/src/proto/vim9execute.pro +++ b/src/proto/vim9execute.pro @@ -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); diff --git a/src/proto/vim9instr.pro b/src/proto/vim9instr.pro index 887c2f61b7..8fd3861bf1 100644 --- a/src/proto/vim9instr.pro +++ b/src/proto/vim9instr.pro @@ -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); diff --git a/src/regexp.c b/src/regexp.c index 8e6e9c4405..61fc14da32 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -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; diff --git a/src/structs.h b/src/structs.h index 659d92434e..2b71618c19 100644 --- a/src/structs.h +++ b/src/structs.h @@ -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 diff --git a/src/testdir/dumps/Test_prop_with_text_above_1a.dump b/src/testdir/dumps/Test_prop_with_text_above_1a.dump new file mode 100644 index 0000000000..c56d88f62e --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_above_1a.dump @@ -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| diff --git a/src/testdir/dumps/Test_prop_with_text_above_1b.dump b/src/testdir/dumps/Test_prop_with_text_above_1b.dump new file mode 100644 index 0000000000..d2d9c7b78e --- /dev/null +++ b/src/testdir/dumps/Test_prop_with_text_above_1b.dump @@ -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| diff --git a/src/testdir/test_cmdline.vim b/src/testdir/test_cmdline.vim index 7febc12269..27ca8bf841 100644 --- a/src/testdir/test_cmdline.vim +++ b/src/testdir/test_cmdline.vim @@ -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', diff --git a/src/testdir/test_filetype.vim b/src/testdir/test_filetype.vim index 62867e3f33..bf0fdd6014 100644 --- a/src/testdir/test_filetype.vim +++ b/src/testdir/test_filetype.vim @@ -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'], diff --git a/src/testdir/test_substitute.vim b/src/testdir/test_substitute.vim index 92e86a9a10..9bbe99763c 100644 --- a/src/testdir/test_substitute.vim +++ b/src/testdir/test_substitute.vim @@ -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>): 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() diff --git a/src/testdir/test_textprop.vim b/src/testdir/test_textprop.vim index d45409ebc5..259ff9c366 100644 --- a/src/testdir/test_textprop.vim +++ b/src/testdir/test_textprop.vim @@ -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 \") diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index 109cb35af6..dccd99bb32 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -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() diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index 0f462e3243..8f9ffad2f6 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -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 @@ -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 \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, "\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()\Bfunc()\enddef\") @@ -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 diff --git a/src/testdir/test_virtualedit.vim b/src/testdir/test_virtualedit.vim index 8fd6d98447..8ca81a777d 100644 --- a/src/testdir/test_virtualedit.vim +++ b/src/testdir/test_virtualedit.vim @@ -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 diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim index cbf2db5633..d0be91ed72 100644 --- a/src/testdir/test_window_cmd.vim +++ b/src/testdir/test_window_cmd.vim @@ -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 :copen - 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 c "copenwincmd k" + 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\" | 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\" | 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() diff --git a/src/userfunc.c b/src/userfunc.c index 1412caa8e0..f0e9cd7c75 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -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) { diff --git a/src/version.c b/src/version.c index f3afefb592..ed3fc789cc 100644 --- a/src/version.c +++ b/src/version.c @@ -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, /**/ diff --git a/src/vim9.h b/src/vim9.h index 1555e4c4a3..51b0346516 100644 --- a/src/vim9.h +++ b/src/vim9.h @@ -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 diff --git a/src/vim9cmds.c b/src/vim9cmds.c index f393afeb48..90758f24f6 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -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" */ diff --git a/src/vim9compile.c b/src/vim9compile.c index 5530056ba5..1a1190e4b6 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -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); } diff --git a/src/vim9execute.c b/src/vim9execute.c index a1c2f8b27c..2448b6d7b1 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -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; diff --git a/src/vim9expr.c b/src/vim9expr.c index 3da556cb10..4406ac47e6 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -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; } } diff --git a/src/vim9instr.c b/src/vim9instr.c index 46f0b367cf..11e39f131b 100644 --- a/src/vim9instr.c +++ b/src/vim9instr.c @@ -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; diff --git a/src/vim9type.c b/src/vim9type.c index b92a7afb15..4484abae90 100644 --- a/src/vim9type.c +++ b/src/vim9type.c @@ -20,6 +20,11 @@ # include #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. diff --git a/src/window.c b/src/window.c index 926ac13a16..195e87ef7d 100644 --- a/src/window.c +++ b/src/window.c @@ -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); } }