# HG changeset patch # User Bram Moolenaar # Date 1587232804 -7200 # Node ID 336483164ca6ae6698cdc223cb74c6afcad614b3 # Parent 61222b5d173d0841972918f2b89d4dbd7a0600c9 patch 8.2.0595: Vim9: not all commands using ends_excmd() tested Commit: https://github.com/vim/vim/commit/a26b9700d73ebccd6c5459d0d66032a4249f6b72 Author: Bram Moolenaar Date: Sat Apr 18 19:53:28 2020 +0200 patch 8.2.0595: Vim9: not all commands using ends_excmd() tested Problem: Vim9: not all commands using ends_excmd() tested. Solution: Find # comment after regular commands. Add more tests. Report error for where it was caused. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -2706,6 +2706,17 @@ common_function(typval_T *argvars, typva TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL, NULL); if (*name != NUL) s = NULL; + else if (trans_name != NULL + && ASCII_ISUPPER(*s) + && current_sctx.sc_version == SCRIPT_VERSION_VIM9 + && find_func(trans_name, NULL) == NULL) + { + // With Vim9 script "MyFunc" can be script-local to the current + // script or global. The script-local name is not found, assume + // global. + vim_free(trans_name); + trans_name = vim_strsave(s); + } } if (s == NULL || *s == NUL || (use_string && VIM_ISDIGIT(*s)) diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -1836,7 +1836,8 @@ do_one_cmd( */ if (*ea.cmd == NUL || *ea.cmd == '"' #ifdef FEAT_EVAL - || (*ea.cmd == '#' && !starts_with_colon && in_vim9script()) + || (*ea.cmd == '#' && ea.cmd[1] != '{' + && !starts_with_colon && in_vim9script()) #endif || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL) { @@ -4436,6 +4437,10 @@ separate_nextcmd(exarg_T *eap) || p != eap->arg) && (eap->cmdidx != CMD_redir || p != eap->arg + 1 || p[-1] != '@')) +#ifdef FEAT_EVAL + || (*p == '#' && in_vim9script() + && p[1] != '{' && p > eap->cmd && VIM_ISWHITE(p[-1])) +#endif || *p == '|' || *p == '\n') { /* @@ -4790,7 +4795,7 @@ ends_excmd2(char_u *cmd_start UNUSED, ch int c = *cmd; #ifdef FEAT_EVAL - if (c == '#' && (cmd == cmd_start || VIM_ISWHITE(cmd[-1]))) + if (c == '#' && cmd[1] != '{' && (cmd == cmd_start || VIM_ISWHITE(cmd[-1]))) return in_vim9script(); #endif return (c == NUL || c == '|' || c == '"' || c == '\n'); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -20,6 +20,7 @@ int call_callback(callback_T *callback, void user_func_error(int error, char_u *name); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe); char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fdp, partial_T **partial); +char_u *untrans_function_name(char_u *name); void ex_function(exarg_T *eap); int eval_fname_script(char_u *p); int translated_function_exists(char_u *name); diff --git a/src/testdir/test_vim9_disassemble.vim b/src/testdir/test_vim9_disassemble.vim --- a/src/testdir/test_vim9_disassemble.vim +++ b/src/testdir/test_vim9_disassemble.vim @@ -251,8 +251,8 @@ def Test_disassemble_pcall() enddef -def FuncWithForwardCall(): string - return DefinedLater("yes") +def s:FuncWithForwardCall(): string + return g:DefinedLater("yes") enddef def DefinedLater(arg: string): string @@ -260,11 +260,11 @@ def DefinedLater(arg: string): string enddef def Test_disassemble_update_instr() - let res = execute('disass FuncWithForwardCall') + let res = execute('disass s:FuncWithForwardCall') assert_match('FuncWithForwardCall.*' .. - 'return DefinedLater("yes").*' .. + 'return g:DefinedLater("yes").*' .. '\d PUSHS "yes".*' .. - '\d UCALL DefinedLater(argc 1).*' .. + '\d UCALL g:DefinedLater(argc 1).*' .. '\d CHECKTYPE string stack\[-1].*' .. '\d RETURN.*', res) @@ -272,9 +272,9 @@ def Test_disassemble_update_instr() " Calling the function will change UCALL into the faster DCALL assert_equal('yes', FuncWithForwardCall()) - res = execute('disass FuncWithForwardCall') + res = execute('disass s:FuncWithForwardCall') assert_match('FuncWithForwardCall.*' .. - 'return DefinedLater("yes").*' .. + 'return g:DefinedLater("yes").*' .. '\d PUSHS "yes".*' .. '\d DCALL DefinedLater(argc 1).*' .. '\d CHECKTYPE string stack\[-1].*' .. diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -587,7 +587,7 @@ def Test_vim9script_fails() CheckScriptFailure(['vim9script', 'export echo 134'], 'E1043:') assert_fails('vim9script', 'E1038') - assert_fails('export something', 'E1042') + assert_fails('export something', 'E1043') enddef def Test_vim9script_reload() @@ -1098,6 +1098,27 @@ def Test_vim9_comment() ], 'E488:') enddef +def Test_vim9_comment_not_compiled() + au TabEnter *.vim let g:entered = 1 + au TabEnter *.x let g:entered = 2 + + edit test.vim + doautocmd TabEnter #comment + assert_equal(1, g:entered) + + doautocmd TabEnter f.x + assert_equal(2, g:entered) + + g:entered = 0 + doautocmd TabEnter f.x #comment + assert_equal(2, g:entered) + + assert_fails('doautocmd Syntax#comment', 'E216:') + + au! TabEnter + unlet g:entered +enddef + " Keep this last, it messes up highlighting. def Test_substitute_cmd() new diff --git a/src/usercmd.c b/src/usercmd.c --- a/src/usercmd.c +++ b/src/usercmd.c @@ -1663,6 +1663,7 @@ do_ucmd(exarg_T *eap) #ifdef FEAT_EVAL current_sctx.sc_sid = cmd->uc_script_ctx.sc_sid; + current_sctx.sc_version = cmd->uc_script_ctx.sc_version; #endif (void)do_cmdline(buf, eap->getline, eap->cookie, DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED); diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -730,7 +730,8 @@ find_func_even_dead(char_u *name, cctx_T } } - hi = hash_find(&func_hashtab, name); + hi = hash_find(&func_hashtab, + STRNCMP(name, "g:", 2) == 0 ? name + 2 : name); if (!HASHITEM_EMPTY(hi)) return HI2UF(hi); @@ -1651,7 +1652,7 @@ 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. + * lower case letter and doesn't contain AUTOLOAD_CHAR or ':'. * "len" is the length of "name", or -1 for NUL terminated. */ int @@ -1659,7 +1660,7 @@ builtin_function(char_u *name, int len) { char_u *p; - if (!ASCII_ISLOWER(name[0])) + if (!ASCII_ISLOWER(name[0]) || name[1] == ':') return FALSE; p = vim_strchr(name, AUTOLOAD_CHAR); return p == NULL || (len > 0 && p > name + len); @@ -1894,6 +1895,15 @@ call_func( // loaded a package, search for the function again fp = find_func(rfname, NULL); } + if (fp == NULL) + { + char_u *p = untrans_function_name(rfname); + + // If using Vim9 script try not local to the script. + // TODO: should not do this if the name started with "s:". + if (p != NULL) + fp = find_func(p, NULL); + } if (fp != NULL && (fp->uf_flags & FC_DELETED)) error = FCERR_DELETED; @@ -2298,6 +2308,27 @@ theend: } /* + * Assuming "name" is the result of trans_function_name() and it was prefixed + * to use the script-local name, return the unmodified name (points into + * "name"). Otherwise return NULL. + * This can be used to first search for a script-local function and fall back + * to the global function if not found. + */ + char_u * +untrans_function_name(char_u *name) +{ + char_u *p; + + if (*name == K_SPECIAL && current_sctx.sc_version == SCRIPT_VERSION_VIM9) + { + p = vim_strchr(name, '_'); + if (p != NULL) + return p + 1; + } + return NULL; +} + +/* * ":function" */ void @@ -2467,6 +2498,16 @@ ex_function(exarg_T *eap) if (!eap->skip && !got_int) { fp = find_func(name, NULL); + if (fp == NULL && ASCII_ISUPPER(*eap->arg)) + { + char_u *up = untrans_function_name(name); + + // With Vim9 script the name was made script-local, if not + // found try again with the original name. + if (p != NULL) + fp = find_func(up, NULL); + } + if (fp != NULL) { list_func_head(fp, TRUE); @@ -2494,7 +2535,7 @@ ex_function(exarg_T *eap) } } else - emsg_funcname(N_("E123: Undefined function: %s"), name); + emsg_funcname(N_("E123: Undefined function: %s"), eap->arg); } goto ret_free; } diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -747,6 +747,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 595, +/**/ 594, /**/ 593, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -2434,8 +2434,10 @@ compile_call(char_u **arg, size_t varlen } // If the name is a variable, load it and use PCALL. + // Not for g:Func(), we don't know if it is a variable or not. p = namebuf; - if (compile_load(&p, namebuf + varlen, cctx, FALSE) == OK) + if (STRNCMP(namebuf, "g:", 2) != 0 + && compile_load(&p, namebuf + varlen, cctx, FALSE) == OK) { res = generate_PCALL(cctx, argcount, FALSE); goto theend; diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -488,6 +488,7 @@ call_def_function( int idx; int ret = FAIL; int defcount = ufunc->uf_args.ga_len - argc; + int save_sc_version = current_sctx.sc_version; // Get pointer to item in the stack. #define STACK_TV(idx) (((typval_T *)ectx.ec_stack.ga_data) + idx) @@ -565,6 +566,9 @@ call_def_function( ectx.ec_instr = dfunc->df_instr; } + // Commands behave like vim9script. + current_sctx.sc_version = SCRIPT_VERSION_VIM9; + // Decide where to start execution, handles optional arguments. init_instr_idx(ufunc, argc, &ectx); @@ -582,6 +586,16 @@ call_def_function( did_throw = TRUE; } + if (did_emsg && msg_list != NULL && *msg_list != NULL) + { + // Turn an error message into an exception. + did_emsg = FALSE; + if (throw_exception(*msg_list, ET_ERROR, NULL) == FAIL) + goto failed; + did_throw = TRUE; + *msg_list = NULL; + } + if (did_throw && !ectx.ec_in_catch) { garray_T *trystack = &ectx.ec_trystack; @@ -1774,6 +1788,7 @@ failed: while (ectx.ec_frame != initial_frame_ptr) func_return(&ectx); failed_early: + current_sctx.sc_version = save_sc_version; for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx) clear_tv(STACK_TV(idx)); vim_free(ectx.ec_stack.ga_data); @@ -1807,6 +1822,14 @@ ex_disassemble(exarg_T *eap) } ufunc = find_func(fname, NULL); + if (ufunc == NULL) + { + char_u *p = untrans_function_name(fname); + + if (p != NULL) + // Try again without making it script-local. + ufunc = find_func(p, NULL); + } vim_free(fname); if (ufunc == NULL) {