# HG changeset patch # User Bram Moolenaar # Date 1641838504 -3600 # Node ID 140102677c12aaca75950351c368966dd8243f70 # Parent 80bbad68ec270525490619d323a6f227019bac07 patch 8.2.4053: Vim9: autoload mechanism doesn't fully work yet Commit: https://github.com/vim/vim/commit/fe2ef0b2cda0b25c45bd9e320f8b77931ee8ef2e Author: Bram Moolenaar Date: Mon Jan 10 18:08:00 2022 +0000 patch 8.2.4053: Vim9: autoload mechanism doesn't fully work yet Problem: Vim9: autoload mechanism doesn't fully work yet. Solution: Define functions and variables with their autoload name, add the prefix when calling a function, find the variable in the table of script variables. diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -3339,10 +3339,12 @@ set_var_const( dictitem_T *di; typval_T *dest_tv = NULL; char_u *varname; + char_u *name_tofree = NULL; hashtab_T *ht = NULL; int is_script_local; int vim9script = in_vim9script(); int var_in_vim9script; + int var_in_autoload = FALSE; int flags = flags_arg; int free_tv_arg = !copy; // free tv_arg if not used @@ -3353,13 +3355,34 @@ set_var_const( varname = name; } else - ht = find_var_ht(name, &varname); + { + if (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid) + && SCRIPT_ITEM(current_sctx.sc_sid)->sn_autoload_prefix != NULL + && is_export) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + size_t len = STRLEN(name) + STRLEN(si->sn_autoload_prefix) + 1; + + // In a vim9 autoload script an exported variable is put in the + // global namespace with the autoload prefix. + var_in_autoload = TRUE; + varname = alloc(len); + if (varname == NULL) + goto failed; + name_tofree = varname; + vim_snprintf((char *)varname, len, "%s%s", + si->sn_autoload_prefix, name); + ht = &globvarht; + } + else + ht = find_var_ht(name, &varname); + } if (ht == NULL || *varname == NUL) { semsg(_(e_illegal_variable_name_str), name); goto failed; } - is_script_local = ht == get_script_local_ht() || sid != 0; + is_script_local = ht == get_script_local_ht() || sid != 0 || var_in_autoload; if (vim9script && !is_script_local @@ -3470,9 +3493,10 @@ set_var_const( // A Vim9 script-local variable is also present in sn_all_vars // and sn_var_vals. It may set "type" from "tv". - if (var_in_vim9script) - update_vim9_script_var(FALSE, di, flags, tv, &type, - (flags & ASSIGN_NO_MEMBER_TYPE) == 0); + if (var_in_vim9script || var_in_autoload) + update_vim9_script_var(FALSE, di, + var_in_autoload ? name : di->di_key, flags, + tv, &type, (flags & ASSIGN_NO_MEMBER_TYPE) == 0); } // existing variable, need to clear the value @@ -3550,10 +3574,11 @@ set_var_const( goto failed; } - // Make sure the variable name is valid. In Vim9 script an autoload - // variable must be prefixed with "g:". + // Make sure the variable name is valid. In Vim9 script an + // autoload variable must be prefixed with "g:" unless in an + // autoload script. if (!valid_varname(varname, -1, !vim9script - || STRNCMP(name, "g:", 2) == 0)) + || STRNCMP(name, "g:", 2) == 0 || var_in_autoload)) goto failed; di = alloc(sizeof(dictitem_T) + STRLEN(varname)); @@ -3571,9 +3596,10 @@ set_var_const( // A Vim9 script-local variable is also added to sn_all_vars and // sn_var_vals. It may set "type" from "tv". - if (var_in_vim9script) - update_vim9_script_var(TRUE, di, flags, tv, &type, - (flags & ASSIGN_NO_MEMBER_TYPE) == 0); + if (var_in_vim9script || var_in_autoload) + update_vim9_script_var(TRUE, di, + var_in_autoload ? name : di->di_key, flags, + tv, &type, (flags & ASSIGN_NO_MEMBER_TYPE) == 0); } dest_tv = &di->di_tv; @@ -3618,6 +3644,7 @@ set_var_const( item_lock(dest_tv, DICT_MAXNEST, TRUE, TRUE); failed: + vim_free(name_tofree); if (free_tv_arg) clear_tv(tv_arg); } diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -38,6 +38,7 @@ void ex_finish(exarg_T *eap); void do_finish(exarg_T *eap, int reanimate); int source_finished(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char_u *script_name_after_autoload(scriptitem_T *si); +char_u *get_autoload_prefix(scriptitem_T *si); char_u *may_prefix_autoload(char_u *name); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro --- a/src/proto/vim9script.pro +++ b/src/proto/vim9script.pro @@ -13,7 +13,7 @@ void mark_imports_for_reload(int sid); void ex_import(exarg_T *eap); int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type, cctx_T *cctx, int verbose); char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg); -void update_vim9_script_var(int create, dictitem_T *di, int flags, typval_T *tv, type_T **type, int do_member); +void update_vim9_script_var(int create, dictitem_T *di, char_u *name, int flags, typval_T *tv, type_T **type, int do_member); void hide_script_var(scriptitem_T *si, int idx, int func_defined); svar_T *find_typval_in_script(typval_T *dest, scid_T sid); int check_script_var_type(svar_T *sv, typval_T *value, char_u *name, where_T where); diff --git a/src/scriptfile.c b/src/scriptfile.c --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -1711,6 +1711,7 @@ free_scriptnames(void) # ifdef FEAT_PROFILE ga_clear(&si->sn_prl_ga); # endif + vim_free(si->sn_autoload_prefix); vim_free(si); } ga_clear(&script_items); @@ -2142,6 +2143,41 @@ script_name_after_autoload(scriptitem_T } /* + * For an autoload script "autoload/dir/script.vim" return the prefix + * "dir#script#" in allocated memory. + * Returns NULL if anything is wrong. + */ + char_u * +get_autoload_prefix(scriptitem_T *si) +{ + char_u *p = script_name_after_autoload(si); + char_u *prefix; + + if (p == NULL) + return NULL; + prefix = vim_strsave(p); + if (prefix == NULL) + return NULL; + + // replace all '/' with '#' and locate ".vim" at the end + for (p = prefix; *p != NUL; p += mb_ptr2len(p)) + { + if (vim_ispathsep(*p)) + *p = '#'; + else if (STRCMP(p, ".vim") == 0) + { + p[0] = '#'; + p[1] = NUL; + return prefix; + } + } + + // did not find ".vim" at the end + vim_free(prefix); + return NULL; +} + +/* * If in a Vim9 autoload script return "name" with the autoload prefix for the * script. If successful "name" is freed, the returned name is allocated. * Otherwise it returns "name" unmodified. @@ -2153,37 +2189,28 @@ may_prefix_autoload(char_u *name) { scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_is_autoload) + if (si->sn_autoload_prefix != NULL) { - char_u *p = script_name_after_autoload(si); + char_u *basename = name; + size_t len; + char_u *res; - if (p != NULL) + if (*name == K_SPECIAL) { - char_u *tail = vim_strsave(p); + char_u *p = vim_strchr(name, '_'); - if (tail != NULL) - { - for (p = tail; *p != NUL; p += mb_ptr2len(p)) - { - if (vim_ispathsep(*p)) - *p = '#'; - else if (STRCMP(p, ".vim")) - { - size_t len = (p - tail) + STRLEN(name) + 2; - char_u *res = alloc(len); + // skip over "99_" + if (p != NULL) + basename = p + 1; + } - if (res == NULL) - break; - *p = NUL; - vim_snprintf((char *)res, len, "%s#%s", tail, name); - vim_free(name); - vim_free(tail); - return res; - } - } - } - // did not find ".vim" at the end - vim_free(tail); + len = STRLEN(si->sn_autoload_prefix) + STRLEN(basename) + 2; + res = alloc(len); + if (res != NULL) + { + vim_snprintf((char *)res, len, "%s%s", + si->sn_autoload_prefix, basename); + return res; } } } diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -1864,7 +1864,9 @@ typedef struct int sn_state; // SN_STATE_ values char_u *sn_save_cpo; // 'cpo' value when :vim9script found char sn_is_vimrc; // .vimrc file, do not restore 'cpo' - char sn_is_autoload; // "vim9script autoload" + + // for "vim9script autoload" this is "dir#scriptname#" + char_u *sn_autoload_prefix; # ifdef FEAT_PROFILE int sn_prof_on; // TRUE when script is/was profiled 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 @@ -3049,6 +3049,14 @@ def Test_vim9_autoload() assert_false(exists('g:prefixed_loaded')) assert_equal('test', prefixed.Gettest()) assert_equal('yes', g:prefixed_loaded) + assert_equal('name', prefixed.name) + END + CheckScriptSuccess(lines) + + # can also get the items by autoload name + lines =<< trim END + call assert_equal('test', prefixed#Gettest()) + call assert_equal('name', prefixed#name) END CheckScriptSuccess(lines) diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -4080,8 +4080,11 @@ define_function(exarg_T *eap, char_u *na eap->skip = TRUE; } -// if (is_export) -// name = may_prefix_autoload(name); + // For "export def FuncName()" in an autoload script the function name + // is stored with the legacy autoload name "dir#script#FuncName" so + // that it can also be found in legacy script. + if (is_export) + name = may_prefix_autoload(name); } // An error in a function call during evaluation of an expression in magic diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4053, +/**/ 4052, /**/ 4051, diff --git a/src/vim9script.c b/src/vim9script.c --- a/src/vim9script.c +++ b/src/vim9script.c @@ -132,7 +132,8 @@ ex_vim9script(exarg_T *eap UNUSED) } si->sn_state = SN_STATE_HAD_COMMAND; - si->sn_is_autoload = found_autoload; + // Store the prefix with the script. It isused to find exported functions. + si->sn_autoload_prefix = get_autoload_prefix(si); current_sctx.sc_version = SCRIPT_VERSION_VIM9; si->sn_version = SCRIPT_VERSION_VIM9; @@ -663,22 +664,37 @@ find_exported( } else { + size_t len = STRLEN(name); char_u buffer[200]; char_u *funcname; - // it could be a user function. - if (STRLEN(name) < sizeof(buffer) - 15) + // It could be a user function. Normally this is stored as + // "99_name". For an autoload script a function is stored with + // the autoload prefix: "dir#script#name". + if (script->sn_autoload_prefix != NULL) + len += STRLEN(script->sn_autoload_prefix) + 2; + else + len += 15; + + if (len < sizeof(buffer)) funcname = buffer; else { - funcname = alloc(STRLEN(name) + 15); + funcname = alloc(len); if (funcname == NULL) return -1; } - funcname[0] = K_SPECIAL; - funcname[1] = KS_EXTRA; - funcname[2] = (int)KE_SNR; - sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); + if (script->sn_autoload_prefix != NULL) + { + sprintf((char *)funcname, "%s%s", script->sn_autoload_prefix, name); + } + else + { + funcname[0] = K_SPECIAL; + funcname[1] = KS_EXTRA; + funcname[2] = (int)KE_SNR; + sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); + } *ufunc = find_func(funcname, FALSE, NULL); if (funcname != buffer) vim_free(funcname); @@ -782,6 +798,7 @@ vim9_declare_scriptvar(exarg_T *eap, cha update_vim9_script_var( int create, dictitem_T *di, + char_u *name, int flags, typval_T *tv, type_T **type, @@ -801,7 +818,7 @@ update_vim9_script_var( if (ga_grow(&si->sn_var_vals, 1) == FAIL) return; - hi = hash_find(&si->sn_all_vars.dv_hashtab, di->di_key); + hi = hash_find(&si->sn_all_vars.dv_hashtab, name); if (!HASHITEM_EMPTY(hi)) { // Variable with this name exists, either in this block or in @@ -833,7 +850,7 @@ update_vim9_script_var( // svar_T and create a new sallvar_T. sv = ((svar_T *)si->sn_var_vals.ga_data) + si->sn_var_vals.ga_len; newsav = (sallvar_T *)alloc_clear( - sizeof(sallvar_T) + STRLEN(di->di_key)); + sizeof(sallvar_T) + STRLEN(name)); if (newsav == NULL) return; @@ -843,7 +860,7 @@ update_vim9_script_var( sv->sv_export = is_export; newsav->sav_var_vals_idx = si->sn_var_vals.ga_len; ++si->sn_var_vals.ga_len; - STRCPY(&newsav->sav_key, di->di_key); + STRCPY(&newsav->sav_key, name); sv->sv_name = newsav->sav_key; newsav->sav_di = di; newsav->sav_block_id = si->sn_current_block_id;