diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 3850b38605..403f592fce 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -5247,6 +5247,8 @@ maparg({name} [, {mode} [, {abbr} [, {dict}]]]) *maparg()* (|mapmode-ic|) "sid" The script local ID, used for mappings (||). + "scriptversion" The version of the script. 999999 for + |Vim9| script. "lnum" The line number in "sid", zero if unknown. "nowait" Do not wait for other, longer mappings. (|:map-|). diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt index 8ea19584b5..9a82ba463d 100644 --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -365,12 +365,11 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. Vim version, or update Vim to a newer version. See |vimscript-version| for what changed between versions. -:vim9s[cript] [noclear] [autoload] *:vim9s* *:vim9script* +:vim9s[cript] [noclear] *:vim9s* *:vim9script* Marks a script file as containing |Vim9-script| commands. Also see |vim9-namespace|. Must be the first command in the file. For [noclear] see |vim9-reload|. - For [autoload] see |vim9-autoload|. Without the |+eval| feature this changes the syntax for some commands. See |:vim9cmd| for executing one command with Vim9 @@ -378,8 +377,11 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|. *:scr* *:scriptnames* :scr[iptnames] List all sourced script names, in the order they were - first sourced. The number is used for the script ID - ||. + first encountered. The number is used for the script + ID ||. + For a script that was used with `import autoload` but + was not actually sourced yet an "A" is shown after the + script ID. {not available when compiled without the |+eval| feature} diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 94a505773f..bbdc2bdf40 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 8.2. Last change: 2022 Jan 15 +*vim9.txt* For Vim version 8.2. Last change: 2022 Jan 18 VIM REFERENCE MANUAL by Bram Moolenaar @@ -1523,17 +1523,18 @@ actually needed. Using the autoload mechanism is recommended: directory. 2. In the autoload script put the bulk of the code. > - vim9script autoload + vim9script export def Stuff(arg: string) ... < This goes in .../autoload/for/search.vim. - Adding "autoload" to `:vim9script` has the effect that "for#search#" will - be prefixed to every exported item. The prefix is obtained from the file - name, as you would to manually in a legacy autoload script. Thus the - exported function can be found with "for#search#Stuff", but you would - normally use `import autoload` and not need to specify the prefix. + Putting the "search.vim" script under the "/autoload/for/" directory has + the effect that "for#search#" will be prefixed to every exported item. The + prefix is obtained from the file name, as you would to manually in a + legacy autoload script. Thus the exported function can be found with + "for#search#Stuff", but you would normally use `import autoload` and not + use the prefix. You can split up the functionality and import other scripts from the autoload script as you like. This way you can share code between plugins. diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 0f6f676c49..b33d24b1ab 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -508,7 +508,8 @@ NETBEANS_LIB = WSock32.lib Ws2_32.lib # gdi32.lib and comdlg32.lib for printing support # ole32.lib and uuid.lib are needed for FEAT_SHORTCUT CON_LIB = oldnames.lib kernel32.lib advapi32.lib shell32.lib gdi32.lib \ - comdlg32.lib ole32.lib netapi32.lib uuid.lib /machine:$(CPU) + comdlg32.lib ole32.lib netapi32.lib uuid.lib user32.lib \ + /machine:$(CPU) !if "$(DELAYLOAD)" == "yes" CON_LIB = $(CON_LIB) /DELAYLOAD:comdlg32.dll /DELAYLOAD:ole32.dll DelayImp.lib !endif @@ -671,6 +672,7 @@ CFLAGS = $(CFLAGS) /fsanitize=address !endif !ifdef NODEBUG + VIM = vim ! if "$(OPTIMIZE)" == "SPACE" OPTFLAG = /O1 @@ -701,7 +703,9 @@ LIBC = msvcrt.lib LIBC = libcmt.lib CFLAGS = $(CFLAGS) /Zl /MT ! endif + !else # DEBUG + VIM = vimd ! if ("$(CPU)" == "i386") || ("$(CPU)" == "ix86") DEBUGINFO = /ZI @@ -721,6 +725,7 @@ LIBC = $(LIBC) msvcrtd.lib LIBC = $(LIBC) libcmtd.lib CFLAGS = $(CFLAGS) /Zl /MTd ! endif + !endif # DEBUG !if "$(CL)" == "/D_USING_V110_SDK71_" @@ -910,9 +915,7 @@ GUI_OBJ = \ $(OUTDIR)\gui_beval.obj \ $(OUTDIR)\gui_w32.obj GUI_LIB = \ - gdi32.lib version.lib $(IME_LIB) \ - winspool.lib comctl32.lib advapi32.lib shell32.lib netapi32.lib \ - /machine:$(CPU) + version.lib $(IME_LIB) winspool.lib comctl32.lib !else SUBSYSTEM = console CUI_INCL = iscygpty.h @@ -1320,11 +1323,11 @@ conflags = $(conflags) /map /mapinfo:lines !ENDIF LINKARGS1 = $(linkdebug) $(conflags) -LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) user32.lib \ +LINKARGS2 = $(CON_LIB) $(GUI_LIB) $(NODEFAULTLIB) $(LIBC) $(OLE_LIB) \ $(LUA_LIB) $(MZSCHEME_LIB) $(PERL_LIB) $(PYTHON_LIB) $(PYTHON3_LIB) $(RUBY_LIB) \ $(TCL_LIB) $(SOUND_LIB) $(NETBEANS_LIB) $(XPM_LIB) $(SOD_LIB) $(LINK_PDB) -# Report link time code generation progress if used. +# Report link time code generation progress if used. !ifdef NODEBUG ! if $(MSVC_MAJOR) >= 8 ! if "$(OPTIMIZE)" != "SPACE" diff --git a/src/auto/configure b/src/auto/configure index b3db0e3c56..f89519543c 100755 --- a/src/auto/configure +++ b/src/auto/configure @@ -5321,6 +5321,12 @@ esac +if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + has_eval=no +else + has_eval=yes +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking --with-compiledby argument" >&5 $as_echo_n "checking --with-compiledby argument... " >&6; } @@ -5398,7 +5404,7 @@ fi $as_echo "$enable_luainterp" >&6; } if test "$enable_luainterp" = "yes" -o "$enable_luainterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then as_fn_error $? "cannot use Lua with tiny or small features" "$LINENO" 5 fi @@ -6182,7 +6188,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_perlinterp" >&5 $as_echo "$enable_perlinterp" >&6; } if test "$enable_perlinterp" = "yes" -o "$enable_perlinterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then as_fn_error $? "cannot use Perl with tiny or small features" "$LINENO" 5 fi @@ -6400,7 +6406,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_pythoninterp" >&5 $as_echo "$enable_pythoninterp" >&6; } if test "$enable_pythoninterp" = "yes" -o "$enable_pythoninterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then as_fn_error $? "cannot use Python with tiny or small features" "$LINENO" 5 fi @@ -6758,7 +6764,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_python3interp" >&5 $as_echo "$enable_python3interp" >&6; } if test "$enable_python3interp" = "yes" -o "$enable_python3interp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then as_fn_error $? "cannot use Python with tiny or small features" "$LINENO" 5 fi @@ -7731,7 +7737,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_rubyinterp" >&5 $as_echo "$enable_rubyinterp" >&6; } if test "$enable_rubyinterp" = "yes" -o "$enable_rubyinterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then as_fn_error $? "cannot use Ruby with tiny or small features" "$LINENO" 5 fi @@ -7936,7 +7942,7 @@ else fi if test "$enable_netbeans" = "yes"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use NetBeans with tiny or small features" >&5 $as_echo "cannot use NetBeans with tiny or small features" >&6; } enable_netbeans="no" @@ -7959,7 +7965,7 @@ else fi if test "$enable_channel" = "yes"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use channels with tiny or small features" >&5 $as_echo "cannot use channels with tiny or small features" >&6; } enable_channel="no" @@ -8263,7 +8269,7 @@ else fi if test "$enable_terminal" = "yes" || test "$enable_terminal" = "auto" -a "x$features" = "xhuge" ; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use terminal emulator with tiny or small features" >&5 $as_echo "cannot use terminal emulator with tiny or small features" >&6; } enable_terminal="no" @@ -13280,8 +13286,14 @@ $as_echo "Defaulting to no" >&6; } enable_canberra="no" fi else - { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_canberra" >&5 + if test "$enable_canberra" = "yes" -a "$has_eval" = "no"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: cannot use sound with tiny or small features" >&5 +$as_echo "cannot use sound with tiny or small features" >&6; } + enable_canberra="no" + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_canberra" >&5 $as_echo "$enable_canberra" >&6; } + fi fi if test "$enable_canberra" = "yes"; then if test "x$PKG_CONFIG" != "xno"; then @@ -13296,7 +13308,7 @@ if test "$enable_canberra" = "yes"; then $as_echo_n "checking for libcanberra... " >&6; } ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" - if `echo "$CFLAGS" | grep -v "$canberra_cflags" >/dev/null`; then + if `echo "$CFLAGS" | grep -v "$canberra_cflags" 2>/dev/null`; then CFLAGS="$CFLAGS $canberra_cflags" fi LIBS="$LIBS $canberra_lib" diff --git a/src/configure.ac b/src/configure.ac index 031919e218..20d70e14bd 100644 --- a/src/configure.ac +++ b/src/configure.ac @@ -579,6 +579,12 @@ esac AC_SUBST(dovimdiff) AC_SUBST(dogvimdiff) +if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + has_eval=no +else + has_eval=yes +fi + AC_MSG_CHECKING(--with-compiledby argument) AC_ARG_WITH(compiledby, [ --with-compiledby=NAME name to show in :version message], compiledby="$withval"; AC_MSG_RESULT($withval), @@ -624,7 +630,7 @@ AC_ARG_ENABLE(luainterp, AC_MSG_RESULT($enable_luainterp) if test "$enable_luainterp" = "yes" -o "$enable_luainterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_ERROR([cannot use Lua with tiny or small features]) fi @@ -1122,7 +1128,7 @@ AC_ARG_ENABLE(perlinterp, [enable_perlinterp="no"]) AC_MSG_RESULT($enable_perlinterp) if test "$enable_perlinterp" = "yes" -o "$enable_perlinterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_ERROR([cannot use Perl with tiny or small features]) fi AC_SUBST(vi_cv_path_perl) @@ -1282,7 +1288,7 @@ AC_ARG_ENABLE(pythoninterp, [enable_pythoninterp="no"]) AC_MSG_RESULT($enable_pythoninterp) if test "$enable_pythoninterp" = "yes" -o "$enable_pythoninterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_ERROR([cannot use Python with tiny or small features]) fi @@ -1527,7 +1533,7 @@ AC_ARG_ENABLE(python3interp, [enable_python3interp="no"]) AC_MSG_RESULT($enable_python3interp) if test "$enable_python3interp" = "yes" -o "$enable_python3interp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_ERROR([cannot use Python with tiny or small features]) fi @@ -2074,7 +2080,7 @@ AC_ARG_ENABLE(rubyinterp, [enable_rubyinterp="no"]) AC_MSG_RESULT($enable_rubyinterp) if test "$enable_rubyinterp" = "yes" -o "$enable_rubyinterp" = "dynamic"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_ERROR([cannot use Ruby with tiny or small features]) fi @@ -2213,7 +2219,7 @@ AC_ARG_ENABLE(netbeans, [ --disable-netbeans Disable NetBeans integration support.], , [enable_netbeans="yes"]) if test "$enable_netbeans" = "yes"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_RESULT([cannot use NetBeans with tiny or small features]) enable_netbeans="no" else @@ -2228,7 +2234,7 @@ AC_ARG_ENABLE(channel, [ --disable-channel Disable process communication support.], , [enable_channel="yes"]) if test "$enable_channel" = "yes"; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_RESULT([cannot use channels with tiny or small features]) enable_channel="no" else @@ -2336,7 +2342,7 @@ AC_ARG_ENABLE(terminal, [ --enable-terminal Enable terminal emulation support.], , [enable_terminal="auto"]) if test "$enable_terminal" = "yes" || test "$enable_terminal" = "auto" -a "x$features" = "xhuge" ; then - if test "x$features" = "xtiny" -o "x$features" = "xsmall"; then + if test "$has_eval" = "no"; then AC_MSG_RESULT([cannot use terminal emulator with tiny or small features]) enable_terminal="no" else @@ -3979,7 +3985,12 @@ if test "$enable_canberra" = "maybe"; then enable_canberra="no" fi else - AC_MSG_RESULT($enable_canberra) + if test "$enable_canberra" = "yes" -a "$has_eval" = "no"; then + AC_MSG_RESULT([cannot use sound with tiny or small features]) + enable_canberra="no" + else + AC_MSG_RESULT($enable_canberra) + fi fi if test "$enable_canberra" = "yes"; then if test "x$PKG_CONFIG" != "xno"; then @@ -3993,7 +4004,7 @@ if test "$enable_canberra" = "yes"; then AC_MSG_CHECKING(for libcanberra) ac_save_CFLAGS="$CFLAGS" ac_save_LIBS="$LIBS" - if `echo "$CFLAGS" | grep -v "$canberra_cflags" >/dev/null`; then + if `echo "$CFLAGS" | grep -v "$canberra_cflags" 2>/dev/null`; then CFLAGS="$CFLAGS $canberra_cflags" fi LIBS="$LIBS $canberra_lib" diff --git a/src/errors.h b/src/errors.h index 5c85dac98d..67f12e7478 100644 --- a/src/errors.h +++ b/src/errors.h @@ -3213,8 +3213,7 @@ EXTERN char e_cannot_import_dot_vim_without_using_as[] INIT(= N_("E1261: Cannot import .vim without using \"as\"")); EXTERN char e_cannot_import_same_script_twice_str[] INIT(= N_("E1262: Cannot import the same script twice: %s")); -EXTERN char e_using_autoload_in_script_not_under_autoload_directory[] - INIT(= N_("E1263: Using autoload in a script not under an autoload directory")); +// E1263 unused EXTERN char e_autoload_import_cannot_use_absolute_or_relative_path[] INIT(= N_("E1264: Autoload import cannot use absolute or relative path: %s")); EXTERN char e_cannot_use_partial_here[] diff --git a/src/eval.c b/src/eval.c index a21fbba851..2b145e1a2a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -625,6 +625,63 @@ eval_expr(char_u *arg, exarg_T *eap) return tv; } +/* + * "*arg" points to what can be a function name in the form of "import.Name" or + * "Funcref". Return the name of the function. Set "tofree" to something that + * was allocated. + * If "verbose" is FALSE no errors are given. + * Return NULL for any failure. + */ + static char_u * +deref_function_name( + char_u **arg, + char_u **tofree, + evalarg_T *evalarg, + int verbose) +{ + typval_T ref; + char_u *name = *arg; + + ref.v_type = VAR_UNKNOWN; + if (eval7(arg, &ref, evalarg, FALSE) == FAIL) + return NULL; + if (*skipwhite(*arg) != NUL) + { + if (verbose) + semsg(_(e_trailing_characters_str), *arg); + name = NULL; + } + else if (ref.v_type == VAR_FUNC && ref.vval.v_string != NULL) + { + name = ref.vval.v_string; + ref.vval.v_string = NULL; + *tofree = name; + } + else if (ref.v_type == VAR_PARTIAL && ref.vval.v_partial != NULL) + { + if (ref.vval.v_partial->pt_argc > 0 + || ref.vval.v_partial->pt_dict != NULL) + { + if (verbose) + emsg(_(e_cannot_use_partial_here)); + name = NULL; + } + else + { + name = vim_strsave(partial_name(ref.vval.v_partial)); + *tofree = name; + } + } + else + { + if (verbose) + semsg(_(e_not_callable_type_str), name); + name = NULL; + } + clear_tv(&ref); + return name; +} + /* * Call some Vim script function and return the result in "*rettv". * Uses argv[0] to argv[argc - 1] for the function arguments. argv[argc] @@ -640,15 +697,29 @@ call_vim_function( { int ret; funcexe_T funcexe; + char_u *arg; + char_u *name; + char_u *tofree = NULL; rettv->v_type = VAR_UNKNOWN; // clear_tv() uses this CLEAR_FIELD(funcexe); funcexe.fe_firstline = curwin->w_cursor.lnum; funcexe.fe_lastline = curwin->w_cursor.lnum; funcexe.fe_evaluate = TRUE; - ret = call_func(func, -1, rettv, argc, argv, &funcexe); + + // The name might be "import.Func" or "Funcref". + arg = func; + ++emsg_off; + name = deref_function_name(&arg, &tofree, &EVALARG_EVALUATE, FALSE); + --emsg_off; + if (name == NULL) + name = func; + + ret = call_func(name, -1, rettv, argc, argv, &funcexe); + if (ret == FAIL) clear_tv(rettv); + vim_free(tofree); return ret; } @@ -3979,57 +4050,21 @@ eval_method( if (**arg != '(' && alias == NULL && (paren = vim_strchr(*arg, '(')) != NULL) { - typval_T ref; + char_u *deref; *arg = name; *paren = NUL; - ref.v_type = VAR_UNKNOWN; - if (eval7(arg, &ref, evalarg, FALSE) == FAIL) + deref = deref_function_name(arg, &tofree, evalarg, verbose); + if (deref == NULL) { *arg = name + len; ret = FAIL; } - else if (*skipwhite(*arg) != NUL) - { - if (verbose) - semsg(_(e_trailing_characters_str), *arg); - ret = FAIL; - } - else if (ref.v_type == VAR_FUNC && ref.vval.v_string != NULL) - { - name = ref.vval.v_string; - ref.vval.v_string = NULL; - tofree = name; - len = STRLEN(name); - } - else if (ref.v_type == VAR_PARTIAL && ref.vval.v_partial != NULL) - { - if (ref.vval.v_partial->pt_argc > 0 - || ref.vval.v_partial->pt_dict != NULL) - { - emsg(_(e_cannot_use_partial_here)); - ret = FAIL; - } - else - { - name = vim_strsave(partial_name(ref.vval.v_partial)); - tofree = name; - if (name == NULL) - { - ret = FAIL; - name = *arg; - } - else - len = STRLEN(name); - } - } else { - if (verbose) - semsg(_(e_not_callable_type_str), name); - ret = FAIL; + name = deref; + len = STRLEN(name); } - clear_tv(&ref); *paren = '('; } diff --git a/src/if_tcl.c b/src/if_tcl.c index 75f904a9ef..f66bfd3860 100644 --- a/src/if_tcl.c +++ b/src/if_tcl.c @@ -248,14 +248,6 @@ vim_tcl_init(char *arg) #endif } -#if defined(EXITFREE) || defined(PROTO) - void -vim_tcl_finalize(void) -{ - Tcl_Finalize(); -} -#endif - #if defined(DYNAMIC_TCL) || defined(PROTO) static int stubs_initialized = FALSE; @@ -285,6 +277,17 @@ tcl_enabled(int verbose) } return stubs_initialized; } +#endif + +#if defined(EXITFREE) || defined(PROTO) + void +vim_tcl_finalize(void) +{ +# ifdef DYNAMIC_TCL + if (stubs_initialized) +# endif + Tcl_Finalize(); +} #endif void diff --git a/src/map.c b/src/map.c index 13df40d00b..4e4fd6bc03 100644 --- a/src/map.c +++ b/src/map.c @@ -222,11 +222,12 @@ map_add( #ifdef FEAT_EVAL int expr, scid_T sid, // -1 to use current_sctx + int scriptversion, linenr_T lnum, #endif int simplified) { - mapblock_T *mp = ALLOC_ONE(mapblock_T); + mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T); if (mp == NULL) return FAIL; @@ -259,11 +260,11 @@ map_add( mp->m_simplified = simplified; #ifdef FEAT_EVAL mp->m_expr = expr; - if (sid >= 0) + if (sid > 0) { mp->m_script_ctx.sc_sid = sid; mp->m_script_ctx.sc_lnum = lnum; - mp->m_script_ctx.sc_version = in_vim9script() ? SCRIPT_VERSION_VIM9 : 0; + mp->m_script_ctx.sc_version = scriptversion; } else { @@ -844,7 +845,7 @@ do_map( if (map_add(map_table, abbr_table, keys, rhs, orig_rhs, noremap, nowait, silent, mode, abbrev, #ifdef FEAT_EVAL - expr, /* sid */ -1, /* lnum */ 0, + expr, /* sid */ -1, /* scriptversion */ 0, /* lnum */ 0, #endif did_simplify && keyround == 1) == FAIL) { @@ -1515,6 +1516,12 @@ check_abbr( } if (mp != NULL) { + int noremap; + int silent; +#ifdef FEAT_EVAL + int expr; +#endif + // Found a match: // Insert the rest of the abbreviation in typebuf.tb_buf[]. // This goes from end to start. @@ -1567,8 +1574,14 @@ check_abbr( // insert the last typed char (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); } + + // copy values here, calling eval_map_expr() may make "mp" invalid! + noremap = mp->m_noremap; + silent = mp->m_silent; #ifdef FEAT_EVAL - if (mp->m_expr) + expr = mp->m_expr; + + if (expr) s = eval_map_expr(mp, c); else #endif @@ -1576,11 +1589,11 @@ check_abbr( if (s != NULL) { // insert the to string - (void)ins_typebuf(s, mp->m_noremap, 0, TRUE, mp->m_silent); + (void)ins_typebuf(s, noremap, 0, TRUE, silent); // no abbrev. for these chars typebuf.tb_no_abbr_cnt += (int)STRLEN(s) + j + 1; #ifdef FEAT_EVAL - if (mp->m_expr) + if (expr) vim_free(s); #endif } @@ -1590,7 +1603,7 @@ check_abbr( if (has_mbyte) len = clen; // Delete characters instead of bytes while (len-- > 0) // delete the from string - (void)ins_typebuf(tb, 1, 0, TRUE, mp->m_silent); + (void)ins_typebuf(tb, 1, 0, TRUE, silent); return TRUE; } } @@ -1601,6 +1614,7 @@ check_abbr( /* * Evaluate the RHS of a mapping or abbreviations and take care of escaping * special characters. + * Careful: after this "mp" will be invalid if the mapping was deleted. */ char_u * eval_map_expr( @@ -2289,6 +2303,8 @@ get_maparg(typval_T *argvars, typval_T *rettv, int exact) dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L); dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L); dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid); + dict_add_number(dict, "scriptversion", + (long)mp->m_script_ctx.sc_version); dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum); dict_add_number(dict, "buffer", (long)buffer_local); dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L); @@ -2358,6 +2374,7 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED) int silent; int buffer; scid_T sid; + int scriptversion; linenr_T lnum; mapblock_T **map_table = maphash; mapblock_T **abbr_table = &first_abbr; @@ -2403,6 +2420,7 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED) expr = dict_get_number(d, (char_u *)"expr") != 0; silent = dict_get_number(d, (char_u *)"silent") != 0; sid = dict_get_number(d, (char_u *)"sid"); + scriptversion = dict_get_number(d, (char_u *)"scriptversion"); lnum = dict_get_number(d, (char_u *)"lnum"); buffer = dict_get_number(d, (char_u *)"buffer"); nowait = dict_get_number(d, (char_u *)"nowait") != 0; @@ -2433,10 +2451,11 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED) vim_free(arg); (void)map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs, noremap, - nowait, silent, mode, is_abbr, expr, sid, lnum, 0); + nowait, silent, mode, is_abbr, expr, sid, scriptversion, lnum, 0); if (lhsrawalt != NULL) (void)map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs, noremap, - nowait, silent, mode, is_abbr, expr, sid, lnum, 1); + nowait, silent, mode, is_abbr, expr, sid, scriptversion, + lnum, 1); vim_free(keys_buf); vim_free(arg_buf); } diff --git a/src/misc2.c b/src/misc2.c index 48fe2c00ea..c1f7e65468 100644 --- a/src/misc2.c +++ b/src/misc2.c @@ -2909,7 +2909,6 @@ mch_parse_cmd(char_u *cmd, int use_shcf, char ***argv, int *argc) return OK; } -# if defined(FEAT_JOB_CHANNEL) || defined(PROTO) /* * Build "argv[argc]" from the string "cmd". * "argv[argc]" is set to NULL; @@ -2936,6 +2935,7 @@ build_argv_from_string(char_u *cmd, char ***argv, int *argc) return OK; } +# if defined(FEAT_JOB_CHANNEL) || defined(PROTO) /* * Build "argv[argc]" from the list "l". * "argv[argc]" is set to NULL; diff --git a/src/ops.c b/src/ops.c index 6a378442c8..a996802490 100644 --- a/src/ops.c +++ b/src/ops.c @@ -536,24 +536,29 @@ block_insert( if (b_insert) { off = (*mb_head_off)(oldp, oldp + offset + spaces); + spaces -= off; + count -= off; } else { - off = (*mb_off_next)(oldp, oldp + offset); - offset += off; + // spaces fill the gap, the character that's at the edge moves + // right + off = (*mb_head_off)(oldp, oldp + offset); + offset -= off; } - spaces -= off; - count -= off; } if (spaces < 0) // can happen when the cursor was moved spaces = 0; - newp = alloc(STRLEN(oldp) + s_len + count + 1); + // Make sure the allocated size matches what is actually copied below. + newp = alloc(STRLEN(oldp) + spaces + s_len + + (spaces > 0 && !bdp->is_short ? ts_val - spaces : 0) + + count + 1); if (newp == NULL) continue; // copy up to shifted part - mch_memmove(newp, oldp, (size_t)(offset)); + mch_memmove(newp, oldp, (size_t)offset); oldp += offset; // insert pre-padding @@ -564,14 +569,21 @@ block_insert( mch_memmove(newp + startcol, s, (size_t)s_len); offset += s_len; - if (spaces && !bdp->is_short) + if (spaces > 0 && !bdp->is_short) { - // insert post-padding - vim_memset(newp + offset + spaces, ' ', (size_t)(ts_val - spaces)); - // We're splitting a TAB, don't copy it. - oldp++; - // We allowed for that TAB, remember this now - count++; + if (*oldp == TAB) + { + // insert post-padding + vim_memset(newp + offset + spaces, ' ', + (size_t)(ts_val - spaces)); + // we're splitting a TAB, don't copy it + oldp++; + // We allowed for that TAB, remember this now + count++; + } + else + // Not a TAB, no extra spaces + count = spaces; } if (spaces > 0) @@ -1598,7 +1610,7 @@ op_insert(oparg_T *oap, long count1) oap->start_vcol = t; } else if (oap->op_type == OP_APPEND - && oap->end.col + oap->end.coladd + && oap->start.col + oap->start.coladd >= curbuf->b_op_start_orig.col + curbuf->b_op_start_orig.coladd) { diff --git a/src/scriptfile.c b/src/scriptfile.c index 350813934a..5b1e43b78f 100644 --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1648,12 +1648,22 @@ ex_scriptnames(exarg_T *eap) } for (i = 1; i <= script_items.ga_len && !got_int; ++i) - if (SCRIPT_ITEM(i)->sn_name != NULL) + { + scriptitem_T *si = SCRIPT_ITEM(i); + + if (si->sn_name != NULL) { - home_replace(NULL, SCRIPT_ITEM(i)->sn_name, - NameBuff, MAXPATHL, TRUE); - smsg("%3d: %s", i, NameBuff); + home_replace(NULL, si->sn_name, NameBuff, MAXPATHL, TRUE); + vim_snprintf((char *)IObuff, IOSIZE, "%3d%s: %s", + i, + si->sn_state == SN_STATE_NOT_LOADED ? " A" : "", + NameBuff); + msg_putchar('\n'); + msg_outtrans(IObuff); + out_flush(); // output one line at a time + ui_breakcheck(); } + } } # if defined(BACKSLASH_IN_FILENAME) || defined(PROTO) diff --git a/src/testdir/test_maparg.vim b/src/testdir/test_maparg.vim index 4cd32a5d3c..64e02a7327 100644 --- a/src/testdir/test_maparg.vim +++ b/src/testdir/test_maparg.vim @@ -18,26 +18,30 @@ func Test_maparg() call assert_equal("isfoo", maparg('foo')) call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', \ 'lhsraw': "foo\x80\xfc\x04V", 'lhsrawalt': "foo\x16", - \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, + \ 'mode': ' ', 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, \ 'rhs': 'isfoo', 'buffer': 0}, \ maparg('foo', '', 0, 1)) call assert_equal({'silent': 1, 'noremap': 1, 'script': 1, 'lhs': 'bar', \ 'lhsraw': 'bar', 'mode': 'v', - \ 'nowait': 0, 'expr': 1, 'sid': sid, 'lnum': lnum + 2, + \ 'nowait': 0, 'expr': 1, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 2, \ 'rhs': 'isbar', 'buffer': 1}, \ 'bar'->maparg('', 0, 1)) let lnum = expand('') map foo bar call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'foo', \ 'lhsraw': 'foo', 'mode': ' ', - \ 'nowait': 1, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'bar', + \ 'nowait': 1, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, 'rhs': 'bar', \ 'buffer': 1}, \ maparg('foo', '', 0, 1)) let lnum = expand('') tmap baz foo call assert_equal({'silent': 0, 'noremap': 0, 'script': 0, 'lhs': 'baz', \ 'lhsraw': 'baz', 'mode': 't', - \ 'nowait': 0, 'expr': 0, 'sid': sid, 'lnum': lnum + 1, 'rhs': 'foo', + \ 'nowait': 0, 'expr': 0, 'sid': sid, 'scriptversion': 1, + \ 'lnum': lnum + 1, 'rhs': 'foo', \ 'buffer': 0}, \ maparg('baz', 't', 0, 1)) diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim index b170b580dc..a556362ba5 100644 --- a/src/testdir/test_mapping.vim +++ b/src/testdir/test_mapping.vim @@ -704,6 +704,11 @@ func Test_mapcomplete() mapclear endfunc +func GetAbbrText() + unabbr hola + return 'hello' +endfunc + " Test for in abbreviation func Test_expr_abbr() new @@ -719,7 +724,14 @@ func Test_expr_abbr() call assert_equal('', getline(1)) unabbr hte - close! + " evaluating the expression deletes the abbreviation + abbr hola GetAbbrText() + call assert_equal('GetAbbrText()', maparg('hola', 'i', '1')) + call feedkeys("ahola \", 'xt') + call assert_equal('hello ', getline('.')) + call assert_equal('', maparg('hola', 'i', '1')) + + bwipe! endfunc " Test for storing mappings in different modes in a vimrc file diff --git a/src/testdir/test_scriptnames.vim b/src/testdir/test_scriptnames.vim index 4712d00a45..44ec146666 100644 --- a/src/testdir/test_scriptnames.vim +++ b/src/testdir/test_scriptnames.vim @@ -23,6 +23,10 @@ func Test_scriptnames() bwipe call delete('Xscripting') + + let msgs = execute('messages') + scriptnames + call assert_equal(msgs, execute('messages')) endfunc " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/testdir/test_usercommands.vim b/src/testdir/test_usercommands.vim index 808a2c7d8c..b3ba620698 100644 --- a/src/testdir/test_usercommands.vim +++ b/src/testdir/test_usercommands.vim @@ -589,10 +589,10 @@ func Test_command_list() \ execute('command DoCmd')) " Test output in verbose mode. - command! DoCmd : + command! -nargs=+ -complete=customlist,SomeFunc DoCmd :ls call assert_match("^\n" \ .. " Name Args Address Complete Definition\n" - \ .. " DoCmd 0 :\n" + \ .. " DoCmd + customlist,SomeFunc :ls\n" \ .. "\tLast set from .*/test_usercommands.vim line \\d\\+$", \ execute('verbose command DoCmd')) diff --git a/src/testdir/test_vim9_builtin.vim b/src/testdir/test_vim9_builtin.vim index eb86a03b91..3747a4e19f 100644 --- a/src/testdir/test_vim9_builtin.vim +++ b/src/testdir/test_vim9_builtin.vim @@ -2278,6 +2278,7 @@ def Test_maparg() nowait: 0, expr: 0, sid: SID(), + scriptversion: 999999, rhs: 'bar', buffer: 0}) unmap foo diff --git a/src/testdir/test_vim9_expr.vim b/src/testdir/test_vim9_expr.vim index 3ea3a0b6bc..6428e6386a 100644 --- a/src/testdir/test_vim9_expr.vim +++ b/src/testdir/test_vim9_expr.vim @@ -1717,7 +1717,7 @@ let g:dict_one = #{one: 1} let $TESTVAR = 'testvar' " type casts -def Test_expr7t() +def Test_expr7() var lines =<< trim END var ls: list = ['a', g:string_empty] var ln: list = [g:anint, g:thefour] @@ -1743,7 +1743,7 @@ def Test_expr7t() enddef " test low level expression -def Test_expr7_number() +def Test_expr8_number() # number constant var lines =<< trim END assert_equal(0, 0) @@ -1756,7 +1756,7 @@ def Test_expr7_number() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_float() +def Test_expr8_float() # float constant if !has('float') MissingFeature 'float' @@ -1771,7 +1771,7 @@ def Test_expr7_float() endif enddef -def Test_expr7_blob() +def Test_expr8_blob() # blob constant var lines =<< trim END assert_equal(g:blob_empty, 0z) @@ -1803,7 +1803,7 @@ def Test_expr7_blob() CheckDefAndScriptFailure(["var x = 0z123"], 'E973:', 1) enddef -def Test_expr7_string() +def Test_expr8_string() # string constant var lines =<< trim END assert_equal(g:string_empty, '') @@ -1820,7 +1820,7 @@ def Test_expr7_string() CheckDefAndScriptFailure(["var x = 'abc"], 'E115:', 1) enddef -def Test_expr7_vimvar() +def Test_expr8_vimvar() v:errors = [] var errs: list = v:errors CheckDefFailure(['var errs: list = v:errors'], 'E1012:') @@ -1845,7 +1845,7 @@ def Test_expr7_vimvar() bwipe! enddef -def Test_expr7_special() +def Test_expr8_special() # special constant var lines =<< trim END assert_equal(g:special_true, true) @@ -1882,7 +1882,7 @@ def Test_expr7_special() CheckDefAndScriptFailure(['v:none = 22'], 'E46:', 1) enddef -def Test_expr7_list() +def Test_expr8_list() # list var lines =<< trim END assert_equal(g:list_empty, []) @@ -1955,7 +1955,7 @@ def Test_expr7_list() CheckDefAndScriptFailure(lines + ['echo numbers[a :b]'], 'E1004:', 4) enddef -def Test_expr7_list_vim9script() +def Test_expr8_list_vim9script() var lines =<< trim END var l = [ 11, @@ -2043,7 +2043,7 @@ def LambdaUsingArg(x: number): func x == 2 enddef -def Test_expr7_lambda() +def Test_expr8_lambda() var lines =<< trim END var La = () => 'result' # comment @@ -2129,7 +2129,7 @@ def Test_expr7_lambda() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_lambda_block() +def Test_expr8_lambda_block() var lines =<< trim END var Func = (s: string): string => { return 'hello ' .. s @@ -2209,7 +2209,7 @@ def NewLambdaUsingArg(x: number): func x == 2 enddef -def Test_expr7_new_lambda() +def Test_expr8_new_lambda() var lines =<< trim END var La = () => 'result' assert_equal('result', La()) @@ -2294,7 +2294,7 @@ def Test_expr7_new_lambda() CheckDefAndScriptFailure(['var Fx = (a) => [0', ' 1]'], 'E696:', 2) enddef -def Test_expr7_lambda_vim9script() +def Test_expr8_lambda_vim9script() var lines =<< trim END var v = 10->((a) => a @@ -2313,7 +2313,7 @@ def Test_expr7_lambda_vim9script() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_funcref() +def Test_expr8_funcref() var lines =<< trim END def RetNumber(): number return 123 @@ -2350,7 +2350,7 @@ enddef let g:test_space_dict = {'': 'empty', ' ': 'space'} let g:test_hash_dict = #{one: 1, two: 2} -def Test_expr7_dict() +def Test_expr8_dict() # dictionary var lines =<< trim END assert_equal(g:dict_empty, {}) @@ -2461,7 +2461,7 @@ def Test_expr7_dict() CheckDefExecAndScriptFailure(['{}[getftype("file")]'], 'E716: Key not present in Dictionary: ""', 1) enddef -def Test_expr7_dict_vim9script() +def Test_expr8_dict_vim9script() var lines =<< trim END var d = { ['one']: @@ -2592,7 +2592,7 @@ def Test_expr7_dict_vim9script() CheckScriptSuccess(lines) enddef -def Test_expr7_dict_in_block() +def Test_expr8_dict_in_block() var lines =<< trim END vim9script command MyCommand { @@ -2615,7 +2615,7 @@ def Test_expr7_dict_in_block() delcommand YourCommand enddef -def Test_expr7_call_2bool() +def Test_expr8_call_2bool() var lines =<< trim END vim9script @@ -2663,7 +2663,7 @@ def Test_expr_member() CheckDefExecAndScriptFailure(["var d: dict", "d = g:list_empty"], 'E1012: Type mismatch; expected dict but got list', 2) enddef -def Test_expr7_any_index_slice() +def Test_expr8_any_index_slice() var lines =<< trim END # getting the one member should clear the list only after getting the item assert_equal('bbb', ['aaa', 'bbb', 'ccc'][1]) @@ -2817,7 +2817,7 @@ def SetSomeVar() b:someVar = &fdm enddef -def Test_expr7_option() +def Test_expr8_option() var lines =<< trim END # option set ts=11 @@ -2844,7 +2844,7 @@ def Test_expr7_option() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_environment() +def Test_expr8_environment() var lines =<< trim END # environment variable assert_equal('testvar', $TESTVAR) @@ -2856,7 +2856,7 @@ def Test_expr7_environment() CheckDefAndScriptFailure(["$"], ['E1002:', 'E15:'], 1) enddef -def Test_expr7_register() +def Test_expr8_register() var lines =<< trim END @a = 'register a' assert_equal('register a', @a) @@ -2882,7 +2882,7 @@ def Test_expr7_register() enddef " This is slow when run under valgrind. -def Test_expr7_namespace() +def Test_expr8_namespace() var lines =<< trim END g:some_var = 'some' assert_equal('some', get(g:, 'some_var')) @@ -2911,7 +2911,7 @@ def Test_expr7_namespace() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_namespace_loop_def() +def Test_expr8_namespace_loop_def() var lines =<< trim END # check using g: in a for loop more than DO_NOT_FREE_CNT times var exists = 0 @@ -2930,8 +2930,8 @@ def Test_expr7_namespace_loop_def() enddef " NOTE: this is known to be slow. To skip use: -" :let $TEST_SKIP_PAT = 'Test_expr7_namespace_loop_script' -def Test_expr7_namespace_loop_script() +" :let $TEST_SKIP_PAT = 'Test_expr8_namespace_loop_script' +def Test_expr8_namespace_loop_script() var lines =<< trim END vim9script # check using g: in a for loop more than DO_NOT_FREE_CNT times @@ -2950,7 +2950,7 @@ def Test_expr7_namespace_loop_script() CheckScriptSuccess(lines) enddef -def Test_expr7_parens() +def Test_expr8_parens() # (expr) var lines =<< trim END assert_equal(4, (6 * 4) / 6) @@ -2982,7 +2982,7 @@ def Test_expr7_parens() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_negate_add() +def Test_expr8_negate_add() var lines =<< trim END assert_equal(-99, -99) assert_equal(-99, - 99) @@ -3031,7 +3031,7 @@ def LegacyReturn(): string legacy return #{key: 'ok'}.key enddef -def Test_expr7_legacy_script() +def Test_expr8_legacy_script() var lines =<< trim END let s:legacy = 'legacy' def GetLocal(): string @@ -3065,7 +3065,7 @@ def s:Echo4Arg(arg: any): string return arg enddef -def Test_expr7_call() +def Test_expr8_call() var lines =<< trim END assert_equal('yes', 'yes'->Echo()) assert_equal(true, !range(5)->empty()) @@ -3098,7 +3098,7 @@ def Test_expr7_call() delete('Xruntime', 'rf') enddef -def Test_expr7_method_call() +def Test_expr8_method_call() var lines =<< trim END new setline(1, ['first', 'last']) @@ -3175,7 +3175,7 @@ def Test_expr7_method_call() CheckDefExecFailure(lines, 'E1013:') enddef -def Test_expr7_method_call_linebreak() +def Test_expr8_method_call_linebreak() # this was giving an error when skipping over the expression var lines =<< trim END vim9script @@ -3191,8 +3191,42 @@ def Test_expr7_method_call_linebreak() CheckScriptSuccess(lines) enddef +def Test_expr8_method_call_import() + var lines =<< trim END + vim9script + export def Square(items: list): list + return map(items, (_, i) => i * i) + enddef + END + call writefile(lines, 'Xsquare.vim') -def Test_expr7_not() + lines =<< trim END + vim9script + import './Xsquare.vim' + + def Test(): list + return range(5) + ->Xsquare.Square() + ->map((_, i) => i * 10) + enddef + + assert_equal([0, 10, 40, 90, 160], Test()) + END + CheckScriptSuccess(lines) + + lines =<< trim END + vim9script + import './Xsquare.vim' + + echo range(5)->Xsquare.NoSuchFunc() + END + CheckScriptFailure(lines, 'E1048: Item not found in script: NoSuchFunc') + + delete('Xsquare.vim') +enddef + + +def Test_expr8_not() var lines =<< trim END assert_equal(true, !'') assert_equal(true, ![]) @@ -3244,7 +3278,7 @@ enddef let g:anumber = 42 -def Test_expr7_negate() +def Test_expr8_negate() var lines =<< trim END var nr = 1 assert_equal(-1, -nr) @@ -3253,7 +3287,7 @@ def Test_expr7_negate() CheckDefAndScriptSuccess(lines) enddef -func Test_expr7_fails() +func Test_expr8_fails() call CheckDefFailure(["var x = (12"], "E1097:", 3) call CheckScriptFailure(['vim9script', "var x = (12"], 'E110:', 2) @@ -3313,7 +3347,7 @@ func CallMe2(one, two) return a:one .. a:two endfunc -def Test_expr7_trailing() +def Test_expr8_trailing() var lines =<< trim END # user function call assert_equal(123, g:CallMe(123)) @@ -3349,7 +3383,7 @@ def Test_expr7_trailing() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_string_subscript() +def Test_expr8_string_subscript() var lines =<< trim END var text = 'abcdef' assert_equal('f', text[-1]) @@ -3448,7 +3482,7 @@ def Test_expr7_string_subscript() CheckDefAndScriptFailure(lines, ['E1012: Type mismatch; expected number but got string', 'E1030: Using a String as a Number: "2"'], 1) enddef -def Test_expr7_list_subscript() +def Test_expr8_list_subscript() var lines =<< trim END var list = [0, 1, 2, 3, 4] assert_equal(0, list[0]) @@ -3491,7 +3525,7 @@ def Test_expr7_list_subscript() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_dict_subscript() +def Test_expr8_dict_subscript() var lines =<< trim END var l = [{lnum: 2}, {lnum: 1}] var res = l[0].lnum > l[1].lnum @@ -3512,7 +3546,7 @@ def Test_expr7_dict_subscript() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_blob_subscript() +def Test_expr8_blob_subscript() var lines =<< trim END var b = 0z112233 assert_equal(0x11, b[0]) @@ -3524,7 +3558,7 @@ def Test_expr7_blob_subscript() CheckDefAndScriptSuccess(lines) enddef -def Test_expr7_subscript_linebreak() +def Test_expr8_subscript_linebreak() var lines =<< trim END var range = range( 3) @@ -3567,7 +3601,7 @@ def Test_expr7_subscript_linebreak() CheckDefAndScriptFailure(lines, ['E1127:', 'E116:'], 2) enddef -func Test_expr7_trailing_fails() +func Test_expr8_trailing_fails() call CheckDefAndScriptFailure(['var l = [2]', 'l->((ll) => add(ll, 8))'], 'E107:', 2) call CheckDefAndScriptFailure(['var l = [2]', 'l->((ll) => add(ll, 8)) ()'], 'E274:', 2) endfunc diff --git a/src/testdir/test_vim9_func.vim b/src/testdir/test_vim9_func.vim index 75d0dd0b65..f495ddfd78 100644 --- a/src/testdir/test_vim9_func.vim +++ b/src/testdir/test_vim9_func.vim @@ -341,6 +341,20 @@ def Test_return_something() ReturnString()->assert_equal('string') ReturnNumber()->assert_equal(123) assert_fails('ReturnGlobal()', 'E1012: Type mismatch; expected number but got string', '', 1, 'ReturnGlobal') + + var lines =<< trim END + vim9script + + def Msg() + echomsg 'in Msg()...' + enddef + + def Func() + return Msg() + enddef + defcompile + END + CheckScriptFailure(lines, 'E1096:') enddef def Test_check_argument_type() diff --git a/src/testdir/test_vim9_import.vim b/src/testdir/test_vim9_import.vim index 36caf70068..3f918f849e 100644 --- a/src/testdir/test_vim9_import.vim +++ b/src/testdir/test_vim9_import.vim @@ -580,6 +580,29 @@ def Test_use_import_in_mapping() nunmap enddef +def Test_use_import_in_completion() + var lines =<< trim END + vim9script + export def Complete(..._): list + return ['abcd'] + enddef + END + writefile(lines, 'Xscript.vim') + + lines =<< trim END + vim9script + import './Xscript.vim' + + command -nargs=1 -complete=customlist,Xscript.Complete Cmd echo 'ok' + feedkeys(":Cmd ab\\#\", 'xnt') + assert_equal('#Cmd abcd', @:) + END + CheckScriptSuccess(lines) + + delcommand Cmd + delete('Xscript.vim') +enddef + def Test_export_fails() CheckScriptFailure(['export var some = 123'], 'E1042:') CheckScriptFailure(['vim9script', 'export var g:some'], 'E1022:') @@ -1173,9 +1196,9 @@ def Test_vim9script_autoload() var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xdir' - # when using "vim9script autoload" prefix is not needed + # when the path has "/autoload/" prefix is not needed var lines =<< trim END - vim9script autoload + vim9script g:prefixed_loaded += 1 export def Gettest(): string @@ -1233,13 +1256,147 @@ def Test_vim9script_autoload() &rtp = save_rtp enddef +def Test_import_autoload_not_exported() + mkdir('Xdir/autoload', 'p') + var save_rtp = &rtp + exe 'set rtp^=' .. getcwd() .. '/Xdir' + + # error when using an item that is not exported from an autoload script + var exportLines =<< trim END + vim9script + var notExported = 123 + def NotExport() + echo 'nop' + enddef + END + writefile(exportLines, 'Xdir/autoload/notExport1.vim') + + var lines =<< trim END + vim9script + import autoload 'notExport1.vim' + echo notExport1.notFound + END + CheckScriptFailure(lines, 'E1048: Item not found in script: notFound') + + lines =<< trim END + vim9script + import autoload 'notExport1.vim' + echo notExport1.notExported + END + CheckScriptFailure(lines, 'E1049: Item not exported in script: notExported') + + lines =<< trim END + vim9script + import autoload 'notExport1.vim' + echo notExport1.NotFunc() + END + CheckScriptFailure(lines, 'E1048: Item not found in script: NotFunc') + + lines =<< trim END + vim9script + import autoload 'notExport1.vim' + echo notExport1.NotExport() + END + CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExport') + + lines =<< trim END + vim9script + import autoload 'notExport1.vim' + echo 'text'->notExport1.NotFunc() + END + CheckScriptFailure(lines, 'E1048: Item not found in script: NotFunc') + + lines =<< trim END + vim9script + import autoload 'notExport1.vim' + echo 'text'->notExport1.NotExport() + END + CheckScriptFailure(lines, 'E1049: Item not exported in script: NotExport') + + # using a :def function we use a different autoload script every time so that + # the function is compiled without the script loaded + writefile(exportLines, 'Xdir/autoload/notExport2.vim') + lines =<< trim END + vim9script + import autoload 'notExport2.vim' + def Testit() + echo notExport2.notFound + enddef + Testit() + END + CheckScriptFailure(lines, 'E1048: Item not found in script: notExport2#notFound') + + writefile(exportLines, 'Xdir/autoload/notExport3.vim') + lines =<< trim END + vim9script + import autoload 'notExport3.vim' + def Testit() + echo notExport3.notExported + enddef + Testit() + END + # don't get E1049 because it is too complicated to figure out + CheckScriptFailure(lines, 'E1048: Item not found in script: notExport3#notExported') + + writefile(exportLines, 'Xdir/autoload/notExport4.vim') + lines =<< trim END + vim9script + import autoload 'notExport4.vim' + def Testit() + echo notExport4.NotFunc() + enddef + Testit() + END + CheckScriptFailure(lines, 'E117: Unknown function: notExport4#NotFunc') + + writefile(exportLines, 'Xdir/autoload/notExport5.vim') + lines =<< trim END + vim9script + import autoload 'notExport5.vim' + def Testit() + echo notExport5.NotExport() + enddef + Testit() + END + CheckScriptFailure(lines, 'E117: Unknown function: notExport5#NotExport') + + writefile(exportLines, 'Xdir/autoload/notExport6.vim') + lines =<< trim END + vim9script + import autoload 'notExport6.vim' + def Testit() + echo 'text'->notExport6.NotFunc() + enddef + Testit() + END + CheckScriptFailure(lines, 'E117: Unknown function: notExport6#NotFunc') + + writefile(exportLines, 'Xdir/autoload/notExport7.vim') + lines =<< trim END + vim9script + import autoload 'notExport7.vim' + def Testit() + echo 'text'->notExport7.NotExport() + enddef + Testit() + END + CheckScriptFailure(lines, 'E117: Unknown function: notExport7#NotExport') + + delete('Xdir', 'rf') + &rtp = save_rtp +enddef + def Test_vim9script_autoload_call() mkdir('Xdir/autoload', 'p') var save_rtp = &rtp exe 'set rtp^=' .. getcwd() .. '/Xdir' var lines =<< trim END - vim9script autoload + vim9script + + export def RetArg(arg: string): string + return arg + enddef export def Getother() g:result = 'other' @@ -1250,6 +1407,13 @@ def Test_vim9script_autoload_call() lines =<< trim END vim9script import autoload 'another.vim' + + # compile this before 'another.vim' is loaded + def CallAnother() + assert_equal('foo', 'foo'->another.RetArg()) + enddef + CallAnother() + call another.Getother() assert_equal('other', g:result) END @@ -1266,7 +1430,7 @@ def Test_import_autoload_postponed() exe 'set rtp^=' .. getcwd() .. '/Xdir' var lines =<< trim END - vim9script autoload + vim9script g:loaded_postponed = 'true' export var variable = 'bla' @@ -1303,7 +1467,7 @@ def Test_import_autoload_override() test_override('autoload', 1) var lines =<< trim END - vim9script autoload + vim9script g:loaded_override = 'true' export var variable = 'bla' @@ -1338,7 +1502,7 @@ def Test_autoload_mapping() exe 'set rtp^=' .. getcwd() .. '/Xdir' var lines =<< trim END - vim9script autoload + vim9script g:toggle_loaded = 'yes' @@ -1363,10 +1527,12 @@ def Test_autoload_mapping() CheckScriptSuccess(lines) assert_false(exists("g:toggle_loaded")) assert_false(exists("g:toggle_called")) + assert_match('\d A: \f*[/\\]toggle.vim', execute('scriptnames')) feedkeys("tt", 'xt') assert_equal('yes', g:toggle_loaded) assert_equal('yes', g:toggle_called) + assert_match('\d: \f*[/\\]toggle.vim', execute('scriptnames')) feedkeys("xx", 'xt') assert_equal('yes', g:doit_called) @@ -1387,7 +1553,13 @@ def Test_vim9script_autoload_fails() vim9script autoload var n = 0 END - CheckScriptFailure(lines, 'E1263:') + CheckScriptFailure(lines, 'E475: Invalid argument: autoload') + + lines =<< trim END + vim9script noclear noclear + var n = 0 + END + CheckScriptFailure(lines, 'E983: Duplicate argument: noclear') enddef def Test_import_autoload_fails() @@ -1399,9 +1571,21 @@ def Test_import_autoload_fails() lines =<< trim END vim9script - import autoload 'doesNotExist.vim' + import autoload './doesNotExist.vim' END CheckScriptFailure(lines, 'E1264:') + + lines =<< trim END + vim9script + import autoload '/dir/doesNotExist.vim' + END + CheckScriptFailure(lines, 'E1264:') + + lines =<< trim END + vim9script + import autoload 'doesNotExist.vim' + END + CheckScriptFailure(lines, 'E1053: Could not import "doesNotExist.vim"') enddef " test disassembling an auto-loaded function starting with "debug" @@ -1468,7 +1652,7 @@ enddef " test using a autoloaded file that is case sensitive def Test_vim9_autoload_case_sensitive() var lines =<< trim END - vim9script autoload + vim9script export def CaseSensitive(): string return 'done' enddef diff --git a/src/testdir/test_visual.vim b/src/testdir/test_visual.vim index e40be5dede..b438fa1e66 100644 --- a/src/testdir/test_visual.vim +++ b/src/testdir/test_visual.vim @@ -1278,6 +1278,17 @@ func Test_visual_block_ctrl_w_f() au! BufNew endfunc +func Test_visual_block_append_invalid_char() + " this was going over the end of the line + set isprint=@,161-255 + new + call setline(1, [' let xxx', 'xxxxxˆ', 'xxxxxxxxxxx']) + exe "normal 0\jjA-\" + call assert_equal([' - let xxx', 'xxxxx -ˆ', 'xxxxxxxx-xxx'], getline(1, 3)) + bwipe! + set isprint& +endfunc + func Test_visual_reselect_with_count() " this was causing an illegal memory access let lines =<< trim END diff --git a/src/usercmd.c b/src/usercmd.c index 70f45aab81..015a9c19c1 100644 --- a/src/usercmd.c +++ b/src/usercmd.c @@ -548,6 +548,15 @@ uc_list(char_u *name, size_t name_len) { STRCPY(IObuff + len, command_complete[j].name); len += (int)STRLEN(IObuff + len); +#ifdef FEAT_EVAL + if (p_verbose > 0 && cmd->uc_compl_arg != NULL + && STRLEN(cmd->uc_compl_arg) < 200) + { + IObuff[len] = ','; + STRCPY(IObuff + len + 1, cmd->uc_compl_arg); + len += (int)STRLEN(IObuff + len); + } +#endif break; } diff --git a/src/userfunc.c b/src/userfunc.c index 0fb042e387..e6c9ab06c2 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -3119,18 +3119,30 @@ free_all_functions(void) /* * Return TRUE if "name" looks like a builtin function name: starts with a - * lower case letter and doesn't contain AUTOLOAD_CHAR or ':'. + * lower case letter, doesn't contain AUTOLOAD_CHAR or ':', no "." after the + * name. * "len" is the length of "name", or -1 for NUL terminated. */ int builtin_function(char_u *name, int len) { - char_u *p; + int i; if (!ASCII_ISLOWER(name[0]) || name[1] == ':') return FALSE; - p = vim_strchr(name, AUTOLOAD_CHAR); - return p == NULL || (len > 0 && p > name + len); + for (i = 0; name[i] != NUL && (len < 0 || i < len); ++i) + { + if (name[i] == AUTOLOAD_CHAR) + return FALSE; + if (!eval_isnamec(name[i])) + { + // "name.something" is not a builtin function + if (name[i] == '.') + return FALSE; + break; + } + } + return TRUE; } int diff --git a/src/version.c b/src/version.c index 4ed39fa742..15da7daf55 100644 --- a/src/version.c +++ b/src/version.c @@ -765,6 +765,52 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 4141, +/**/ + 4140, +/**/ + 4139, +/**/ + 4138, +/**/ + 4137, +/**/ + 4136, +/**/ + 4135, +/**/ + 4134, +/**/ + 4133, +/**/ + 4132, +/**/ + 4131, +/**/ + 4130, +/**/ + 4129, +/**/ + 4128, +/**/ + 4127, +/**/ + 4126, +/**/ + 4125, +/**/ + 4124, +/**/ + 4123, +/**/ + 4122, +/**/ + 4121, +/**/ + 4120, +/**/ + 4119, /**/ 4118, /**/ diff --git a/src/vim9cmds.c b/src/vim9cmds.c index 5beeab82e8..83ff6991bd 100644 --- a/src/vim9cmds.c +++ b/src/vim9cmds.c @@ -2196,6 +2196,11 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx) if (*p != NUL && *p != '|' && *p != '\n') { + if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID) + { + emsg(_(e_returning_value_in_function_without_return_type)); + return NULL; + } if (legacy) { int save_flags = cmdmod.cmod_flags; @@ -2231,13 +2236,6 @@ compile_return(char_u *arg, int check_return_type, int legacy, cctx_T *cctx) } else { - if (cctx->ctx_ufunc->uf_ret_type->tt_type == VAR_VOID - && stack_type->tt_type != VAR_VOID - && stack_type->tt_type != VAR_UNKNOWN) - { - emsg(_(e_returning_value_in_function_without_return_type)); - return NULL; - } if (need_type(stack_type, cctx->ctx_ufunc->uf_ret_type, -1, 0, cctx, FALSE, FALSE) == FAIL) return NULL; diff --git a/src/vim9execute.c b/src/vim9execute.c index c947d6a0ce..5ede04ca8b 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -2200,10 +2200,12 @@ exec_instructions(ectx_T *ectx) case ISN_LOADW: case ISN_LOADT: { - dictitem_T *di = NULL; - hashtab_T *ht = NULL; - char namespace; + dictitem_T *di = NULL; + hashtab_T *ht = NULL; + char namespace; + if (GA_GROW_FAILS(&ectx->ec_stack, 1)) + goto theend; switch (iptr->isn_type) { case ISN_LOADG: @@ -2227,9 +2229,12 @@ exec_instructions(ectx_T *ectx) } di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE); - if (di == NULL && ht == get_globvar_ht()) + if (di == NULL && ht == get_globvar_ht() + && vim_strchr(iptr->isn_arg.string, + AUTOLOAD_CHAR) != NULL) { - // may need to load autoload script + // Global variable has an autoload name, may still need + // to load the script. if (script_autoload(iptr->isn_arg.string, FALSE)) di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE); @@ -2240,14 +2245,19 @@ exec_instructions(ectx_T *ectx) if (di == NULL) { SOURCING_LNUM = iptr->isn_lnum; - semsg(_(e_undefined_variable_char_str), + if (vim_strchr(iptr->isn_arg.string, + AUTOLOAD_CHAR) != NULL) + // no check if the item exists in the script but + // isn't exported, it is too complicated + semsg(_(e_item_not_found_in_script_str), + iptr->isn_arg.string); + else + semsg(_(e_undefined_variable_char_str), namespace, iptr->isn_arg.string); goto on_error; } else { - if (GA_GROW_FAILS(&ectx->ec_stack, 1)) - goto theend; copy_tv(&di->di_tv, STACK_TV_BOT(0)); ++ectx->ec_stack.ga_len; } diff --git a/src/vim9expr.c b/src/vim9expr.c index 4c395785f4..72bfc01e51 100644 --- a/src/vim9expr.c +++ b/src/vim9expr.c @@ -21,6 +21,9 @@ # include "vim9.h" #endif +// flag passed from compile_subscript() to compile_load_scriptvar() +static int paren_follows_after_expr = 0; + /* * Generate code for any ppconst entries. */ @@ -277,7 +280,6 @@ compile_load_scriptvar( int done = FALSE; int res = OK; - // TODO: if this is an autoload import do something else. // Need to lookup the member. if (*p != '.') { @@ -306,7 +308,7 @@ compile_load_scriptvar( // autoload script must be loaded later, access by the autoload // name. - if (cc == '(') + if (cc == '(' || paren_follows_after_expr) res = generate_PUSHFUNC(cctx, auto_name, &t_func_any); else res = generate_LOAD(cctx, ISN_LOADG, 0, auto_name, &t_any); @@ -1732,21 +1734,30 @@ compile_subscript( } else { + int fail; + int save_len = cctx->ctx_ufunc->uf_lines.ga_len; + *paren = NUL; - if (compile_expr8(arg, cctx, ppconst) == FAIL - || *skipwhite(*arg) != NUL) + + // instead of using LOADG for "import.Func" use PUSHFUNC + ++paren_follows_after_expr; + + // do not look in the next line + cctx->ctx_ufunc->uf_lines.ga_len = 1; + + fail = compile_expr8(arg, cctx, ppconst) == FAIL + || *skipwhite(*arg) != NUL; + *paren = '('; + --paren_follows_after_expr; + cctx->ctx_ufunc->uf_lines.ga_len = save_len; + + if (fail) { - *paren = '('; semsg(_(e_invalid_expression_str), pstart); return FAIL; } - *paren = '('; } - // Remember the next instruction index, where the instructions - // for arguments are being written. - expr_isn_end = cctx->ctx_instr.ga_len; - // Compile the arguments. if (**arg != '(') { @@ -1756,6 +1767,11 @@ compile_subscript( semsg(_(e_missing_parenthesis_str), *arg); return FAIL; } + + // Remember the next instruction index, where the instructions + // for arguments are being written. + expr_isn_end = cctx->ctx_instr.ga_len; + *arg = skipwhite(*arg + 1); if (compile_arguments(arg, cctx, &argcount, FALSE) == FAIL) return FAIL; diff --git a/src/vim9script.c b/src/vim9script.c index 2038c78a08..e13ac4ac47 100644 --- a/src/vim9script.c +++ b/src/vim9script.c @@ -69,7 +69,6 @@ ex_vim9script(exarg_T *eap UNUSED) int sid = current_sctx.sc_sid; scriptitem_T *si; int found_noclear = FALSE; - int found_autoload = FALSE; char_u *p; if (!getline_equal(eap->getline, eap->cookie, getsourceline)) @@ -96,20 +95,6 @@ ex_vim9script(exarg_T *eap UNUSED) } found_noclear = TRUE; } - else if (STRNCMP(p, "autoload", 8) == 0 && IS_WHITE_OR_NUL(p[8])) - { - if (found_autoload) - { - semsg(_(e_duplicate_argument_str), p); - return; - } - found_autoload = TRUE; - if (script_name_after_autoload(si) == NULL) - { - emsg(_(e_using_autoload_in_script_not_under_autoload_directory)); - return; - } - } else { semsg(_(e_invalid_argument_str), eap->arg); @@ -411,7 +396,7 @@ handle_import( int ret = FAIL; char_u *as_name = NULL; typval_T tv; - int sid = -1; + int sid = -2; int res; long start_lnum = SOURCING_LNUM; garray_T *import_gap; @@ -468,7 +453,13 @@ handle_import( vim_free(from_name); } } - else if (mch_isFullName(tv.vval.v_string)) + else if (mch_isFullName(tv.vval.v_string) +#ifdef BACKSLASH_IN_FILENAME + // On MS-Windows omitting the drive is still handled like an + // absolute path, not using 'runtimepath'. + || *tv.vval.v_string == '/' || *tv.vval.v_string == '\\' +#endif + ) { // Absolute path: "/tmp/name.vim" if (is_autoload) @@ -519,7 +510,7 @@ handle_import( if (res == FAIL || sid <= 0) { - semsg(_(is_autoload && sid <= 0 + semsg(_(is_autoload && sid == -2 ? e_autoload_import_cannot_use_absolute_or_relative_path : e_could_not_import_str), tv.vval.v_string); goto erret; @@ -716,22 +707,36 @@ find_exported( sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); } *ufunc = find_func(funcname, FALSE); - if (funcname != buffer) - vim_free(funcname); if (*ufunc == NULL) { if (verbose) - semsg(_(e_item_not_found_in_script_str), name); - return -1; + { + ufunc_T *alt_ufunc = NULL; + + if (script->sn_autoload_prefix != NULL) + { + // try find the function by the script-local name + funcname[0] = K_SPECIAL; + funcname[1] = KS_EXTRA; + funcname[2] = (int)KE_SNR; + sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); + alt_ufunc = find_func(funcname, FALSE); + } + if (alt_ufunc != NULL) + semsg(_(e_item_not_exported_in_script_str), name); + else + semsg(_(e_item_not_found_in_script_str), name); + } } else if (((*ufunc)->uf_flags & FC_EXPORT) == 0) { if (verbose) semsg(_(e_item_not_exported_in_script_str), name); *ufunc = NULL; - return -1; } + if (funcname != buffer) + vim_free(funcname); } return idx;