To: vim_dev@googlegroups.com Subject: Patch 8.2.4050 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4050 Problem: Vim9: need to prefix every item in an autoload script. Solution: First step in supporting "vim9script autoload" and "import autoload". Files: runtime/doc/repeat.txt, runtime/doc/vim9.txt, src/structs.h, src/errors.h, src/vim9script.c, src/scriptfile.c, src/proto/scriptfile.pro, src/userfunc.c, src/eval.c, src/evalvars.c, src/vim9compile.c, src/proto/vim9compile.pro, src/vim9expr.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.4049/runtime/doc/repeat.txt 2021-02-14 11:57:32.552655477 +0000 --- runtime/doc/repeat.txt 2022-01-09 14:53:57.822209348 +0000 *************** *** 354,369 **** Vim version, or update Vim to a newer version. See |vimscript-version| for what changed between versions. ! :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|. Without the |+eval| feature this changes the syntax for some commands. See |:vim9cmd| for executing one command with Vim9 syntax and semantics. ! *:scr* *:scriptnames* :scr[iptnames] List all sourced script names, in the order they were first sourced. The number is used for the script ID --- 365,381 ---- Vim version, or update Vim to a newer version. See |vimscript-version| for what changed between versions. ! :vim9s[cript] [noclear] [autoload] *: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 syntax and semantics. ! *:scr* *:scriptnames* :scr[iptnames] List all sourced script names, in the order they were first sourced. The number is used for the script ID *** ../vim-8.2.4049/runtime/doc/vim9.txt 2022-01-06 21:10:24.465027868 +0000 --- runtime/doc/vim9.txt 2022-01-09 15:09:38.686342752 +0000 *************** *** 1484,1520 **** Import in an autoload script ~ ! For optimal startup speed, loading scripts should be postponed until they are ! actually needed. A recommended mechanism: 1. In the plugin define user commands, functions and/or mappings that refer to ! an autoload script. > ! command -nargs=1 SearchForStuff searchfor#Stuff() < This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen. ! 2. In the autoload script do the actual work. You can import items from ! other files to split up functionality in appropriate pieces. > ! vim9script ! import "../import/someother.vim" as other ! def searchfor#Stuff(arg: string) ! var filtered = other.FilterFunc(arg) ... - < This goes in .../autoload/searchfor.vim. "searchfor" in the file name - must be exactly the same as the prefix for the function name, that is how - Vim finds the file. ! 3. Other functionality, possibly shared between plugins, contains the exported ! items and any private items. > ! vim9script ! var localVar = 'local' ! export def FilterFunc(arg: string): string ! ... ! < This goes in .../import/someother.vim. When compiling a `:def` function and a function in an autoload script is encountered, the script is not loaded until the `:def` function is called. Import in legacy Vim script ~ --- 1501,1543 ---- Import in an autoload script ~ ! *vim9-autoload* For optimal startup speed, loading scripts should be postponed until they are ! actually needed. Using the autoload mechanism is recommended: 1. In the plugin define user commands, functions and/or mappings that refer to ! items imported from an autoload script. > ! import autoload 'for/search.vim' ! command -nargs=1 SearchForStuff search.Stuff() < This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen. + The "SearchForStuff" command is now available to the user. ! The "autoload" argument to `:import` means that the script is not loaded ! until one of the items is actually used. The script will be found under ! the "autoload" directory in 'runtimepath' instead of the "import" ! directory. ! ! 2. In the autoload script put the bulk of the code. > ! vim9script autoload ! 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. ! ! 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. When compiling a `:def` function and a function in an autoload script is encountered, the script is not loaded until the `:def` function is called. + This also means you get any errors only at runtime, since the argument and + return types are not known yet. Import in legacy Vim script ~ *** ../vim-8.2.4049/src/structs.h 2022-01-07 15:45:13.495500428 +0000 --- src/structs.h 2022-01-09 19:28:53.695433972 +0000 *************** *** 1827,1832 **** --- 1827,1833 ---- } imported_T; #define IMP_FLAGS_RELOAD 2 // script reloaded, OK to redefine + #define IMP_FLAGS_AUTOLOAD 4 // script still needs to be loaded /* * Info about an already sourced scripts. *************** *** 1863,1868 **** --- 1864,1870 ---- 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" # ifdef FEAT_PROFILE int sn_prof_on; // TRUE when script is/was profiled *************** *** 1886,1892 **** } scriptitem_T; #define SN_STATE_NEW 0 // newly loaded script, nothing done ! #define SN_STATE_RELOAD 1 // script loaded before, nothing done #define SN_STATE_HAD_COMMAND 9 // a command was executed // Struct passed through eval() functions. --- 1888,1895 ---- } scriptitem_T; #define SN_STATE_NEW 0 // newly loaded script, nothing done ! #define SN_STATE_NOT_LOADED 1 // script located but not loaded ! #define SN_STATE_RELOAD 2 // script loaded before, nothing done #define SN_STATE_HAD_COMMAND 9 // a command was executed // Struct passed through eval() functions. *** ../vim-8.2.4049/src/errors.h 2022-01-09 12:49:19.400815606 +0000 --- src/errors.h 2022-01-09 18:26:24.178112518 +0000 *************** *** 3203,3206 **** --- 3203,3210 ---- 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")); + EXTERN char e_autoload_import_cannot_use_absolute_or_relative_path[] + INIT(= N_("E1264: Autoload import cannot use absolute or relative path: %s")); #endif *** ../vim-8.2.4049/src/vim9script.c 2022-01-08 17:03:51.600942321 +0000 --- src/vim9script.c 2022-01-09 19:56:48.099838627 +0000 *************** *** 68,73 **** --- 68,76 ---- #ifdef FEAT_EVAL 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)) { *************** *** 81,92 **** emsg(_(e_vim9script_must_be_first_command_in_script)); return; } ! if (!IS_WHITE_OR_NUL(*eap->arg) && STRCMP(eap->arg, "noclear") != 0) { ! semsg(_(e_invalid_argument_str), eap->arg); ! return; } ! if (si->sn_state == SN_STATE_RELOAD && IS_WHITE_OR_NUL(*eap->arg)) { hashtab_T *ht = &SCRIPT_VARS(sid); --- 84,123 ---- emsg(_(e_vim9script_must_be_first_command_in_script)); return; } ! ! for (p = eap->arg; !IS_WHITE_OR_NUL(*p); p = skipwhite(skiptowhite(p))) { ! if (STRNCMP(p, "noclear", 7) == 0 && IS_WHITE_OR_NUL(p[7])) ! { ! if (found_noclear) ! { ! semsg(_(e_duplicate_argument_str), p); ! return; ! } ! 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); ! return; ! } } ! ! if (si->sn_state == SN_STATE_RELOAD && !found_noclear) { hashtab_T *ht = &SCRIPT_VARS(sid); *************** *** 101,106 **** --- 132,139 ---- } si->sn_state = SN_STATE_HAD_COMMAND; + si->sn_is_autoload = found_autoload; + current_sctx.sc_version = SCRIPT_VERSION_VIM9; si->sn_version = SCRIPT_VERSION_VIM9; *************** *** 366,371 **** --- 399,405 ---- { char_u *arg = arg_start; char_u *nextarg; + int is_autoload = FALSE; int getnext; char_u *expr_end; int ret = FAIL; *************** *** 377,382 **** --- 411,422 ---- garray_T *import_gap; int i; + if (STRNCMP(arg, "autoload", 8) == 0 && VIM_ISWHITE(arg[8])) + { + is_autoload = TRUE; + arg = skipwhite(arg + 8); + } + // The name of the file can be an expression, which must evaluate to a // string. ret = eval0_retarg(arg, &tv, NULL, evalarg, &expr_end); *************** *** 402,424 **** char_u *tail = gettail(si->sn_name); char_u *from_name; ! // Relative to current script: "./name.vim", "../../name.vim". ! len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; ! from_name = alloc((int)len); ! if (from_name == NULL) ! goto erret; ! vim_strncpy(from_name, si->sn_name, tail - si->sn_name); ! add_pathsep(from_name); ! STRCAT(from_name, tv.vval.v_string); ! simplify_filename(from_name); ! res = do_source(from_name, FALSE, DOSO_NONE, &sid); ! vim_free(from_name); } else if (mch_isFullName(tv.vval.v_string)) { // Absolute path: "/tmp/name.vim" ! res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); } else { --- 442,489 ---- char_u *tail = gettail(si->sn_name); char_u *from_name; ! if (is_autoload) ! res = FAIL; ! else ! { ! // Relative to current script: "./name.vim", "../../name.vim". ! len = STRLEN(si->sn_name) - STRLEN(tail) ! + STRLEN(tv.vval.v_string) + 2; ! from_name = alloc((int)len); ! if (from_name == NULL) ! goto erret; ! vim_strncpy(from_name, si->sn_name, tail - si->sn_name); ! add_pathsep(from_name); ! STRCAT(from_name, tv.vval.v_string); ! simplify_filename(from_name); ! ! res = do_source(from_name, FALSE, DOSO_NONE, &sid); ! vim_free(from_name); ! } } else if (mch_isFullName(tv.vval.v_string)) { // Absolute path: "/tmp/name.vim" ! if (is_autoload) ! res = FAIL; ! else ! res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); ! } ! else if (is_autoload) ! { ! size_t len = 9 + STRLEN(tv.vval.v_string) + 1; ! char_u *from_name; ! ! // Find file in "autoload" subdirs in 'runtimepath'. ! from_name = alloc((int)len); ! if (from_name == NULL) ! goto erret; ! vim_snprintf((char *)from_name, len, "autoload/%s", tv.vval.v_string); ! // we need a scriptitem without loading the script ! sid = find_script_in_rtp(from_name); ! vim_free(from_name); ! res = SCRIPT_ID_VALID(sid) ? OK : FAIL; } else { *************** *** 428,436 **** // Find file in "import" subdirs in 'runtimepath'. from_name = alloc((int)len); if (from_name == NULL) - { goto erret; - } vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); vim_free(from_name); --- 493,499 ---- *************** *** 438,444 **** if (res == FAIL || sid <= 0) { ! semsg(_(e_could_not_import_str), tv.vval.v_string); goto erret; } --- 501,509 ---- if (res == FAIL || sid <= 0) { ! semsg(_(is_autoload && sid <= 0 ! ? e_autoload_import_cannot_use_absolute_or_relative_path ! : e_could_not_import_str), tv.vval.v_string); goto erret; } *************** *** 451,457 **** { if (import->imp_flags & IMP_FLAGS_RELOAD) { ! // encountering same script first ime on a reload is OK import->imp_flags &= ~IMP_FLAGS_RELOAD; break; } --- 516,522 ---- { if (import->imp_flags & IMP_FLAGS_RELOAD) { ! // encountering same script first time on a reload is OK import->imp_flags &= ~IMP_FLAGS_RELOAD; break; } *************** *** 513,519 **** { imported_T *imported; ! imported = find_imported(as_name, STRLEN(as_name), cctx); if (imported != NULL && imported->imp_sid != sid) { semsg(_(e_name_already_defined_str), as_name); --- 578,584 ---- { imported_T *imported; ! imported = find_imported(as_name, FALSE, STRLEN(as_name), cctx); if (imported != NULL && imported->imp_sid != sid) { semsg(_(e_name_already_defined_str), as_name); *************** *** 529,534 **** --- 594,601 ---- imported->imp_name = as_name; as_name = NULL; imported->imp_sid = sid; + if (is_autoload) + imported->imp_flags = IMP_FLAGS_AUTOLOAD; } erret: *** ../vim-8.2.4049/src/scriptfile.c 2022-01-08 16:19:18.505639885 +0000 --- src/scriptfile.c 2022-01-09 20:36:52.041830557 +0000 *************** *** 239,244 **** --- 239,340 ---- (void)do_source(fname, FALSE, DOSO_NONE, cookie); } + #ifdef FEAT_EVAL + /* + * Find an already loaded script "name". + * If found returns its script ID. If not found returns -1. + */ + static int + find_script_by_name(char_u *name) + { + int sid; + scriptitem_T *si; + + for (sid = script_items.ga_len; sid > 0; --sid) + { + // We used to check inode here, but that doesn't work: + // - If a script is edited and written, it may get a different + // inode number, even though to the user it is the same script. + // - If a script is deleted and another script is written, with a + // different name, the inode may be re-used. + si = SCRIPT_ITEM(sid); + if (si->sn_name != NULL && fnamecmp(si->sn_name, name) == 0) + return sid; + } + return -1; + } + + /* + * Add a new scriptitem with all items initialized. + * When running out of memory "error" is set to FAIL. + * Returns the script ID. + */ + static int + get_new_scriptitem(int *error) + { + static scid_T last_current_SID = 0; + int sid = ++last_current_SID; + scriptitem_T *si; + + if (ga_grow(&script_items, (int)(sid - script_items.ga_len)) == FAIL) + { + *error = FAIL; + return sid; + } + while (script_items.ga_len < sid) + { + si = ALLOC_CLEAR_ONE(scriptitem_T); + if (si == NULL) + { + *error = FAIL; + return sid; + } + ++script_items.ga_len; + SCRIPT_ITEM(script_items.ga_len) = si; + si->sn_name = NULL; + si->sn_version = 1; + + // Allocate the local script variables to use for this script. + new_script_vars(script_items.ga_len); + ga_init2(&si->sn_var_vals, sizeof(svar_T), 10); + hash_init(&si->sn_all_vars.dv_hashtab); + ga_init2(&si->sn_imports, sizeof(imported_T), 10); + ga_init2(&si->sn_type_list, sizeof(type_T), 10); + # ifdef FEAT_PROFILE + si->sn_prof_on = FALSE; + # endif + } + + // Used to check script variable index is still valid. + si->sn_script_seq = current_sctx.sc_seq; + + return sid; + } + + static void + find_script_callback(char_u *fname, void *cookie) + { + int sid; + int error = OK; + int *ret_sid = cookie; + + sid = find_script_by_name(fname); + if (sid < 0) + { + // script does not exist yet, create a new scriptitem + sid = get_new_scriptitem(&error); + if (error == OK) + { + scriptitem_T *si = SCRIPT_ITEM(sid); + + si->sn_name = vim_strsave(fname); + si->sn_state = SN_STATE_NOT_LOADED; + } + } + *ret_sid = sid; + } + #endif + /* * Find the file "name" in all directories in "path" and invoke * "callback(fname, cookie)". *************** *** 455,461 **** } /* ! * Just like source_runtime(), but use "path" instead of 'runtimepath'. */ int source_in_path(char_u *path, char_u *name, int flags, int *ret_sid) --- 551,558 ---- } /* ! * Just like source_runtime(), but use "path" instead of 'runtimepath' ! * and return the script ID in "ret_sid". */ int source_in_path(char_u *path, char_u *name, int flags, int *ret_sid) *************** *** 463,472 **** return do_in_path_and_pp(path, name, flags, source_callback, ret_sid); } - #if defined(FEAT_EVAL) || defined(PROTO) /* * Expand wildcards in "pat" and invoke do_source() for each match. */ static void --- 560,583 ---- return do_in_path_and_pp(path, name, flags, source_callback, ret_sid); } #if defined(FEAT_EVAL) || defined(PROTO) /* + * Find "name" in 'runtimepath'. If found a new scriptitem is created for it + * and it's script ID is returned. + * If not found returns -1. + */ + int + find_script_in_rtp(char_u *name) + { + int sid = -1; + + (void)do_in_path_and_pp(p_rtp, name, DIP_NOAFTER, + find_script_callback, &sid); + return sid; + } + + /* * Expand wildcards in "pat" and invoke do_source() for each match. */ static void *************** *** 1127,1133 **** int retval = FAIL; sctx_T save_current_sctx; #ifdef FEAT_EVAL - static scid_T last_current_SID = 0; static int last_current_SID_seq = 0; funccal_entry_T funccalp_entry; int save_debug_break_level = debug_break_level; --- 1238,1243 ---- *************** *** 1161,1178 **** estack_compiling = FALSE; // See if we loaded this script before. ! for (sid = script_items.ga_len; sid > 0; --sid) ! { ! // We used to check inode here, but that doesn't work: ! // - If a script is edited and written, it may get a different ! // inode number, even though to the user it is the same script. ! // - If a script is deleted and another script is written, with a ! // different name, the inode may be re-used. ! si = SCRIPT_ITEM(sid); ! if (si->sn_name != NULL && fnamecmp(si->sn_name, fname_exp) == 0) ! // Found it! ! break; ! } if (sid > 0 && ret_sid != NULL) { // Already loaded and no need to load again, return here. --- 1271,1277 ---- estack_compiling = FALSE; // See if we loaded this script before. ! sid = find_script_by_name(fname_exp); if (sid > 0 && ret_sid != NULL) { // Already loaded and no need to load again, return here. *************** *** 1318,1371 **** dictitem_T *di; // loading the same script again - si->sn_state = SN_STATE_RELOAD; current_sctx.sc_sid = sid; ! // Script-local variables remain but "const" can be set again. ! // In Vim9 script variables will be cleared when "vim9script" is ! // encountered without the "noclear" argument. ! ht = &SCRIPT_VARS(sid); ! todo = (int)ht->ht_used; ! for (hi = ht->ht_array; todo > 0; ++hi) ! if (!HASHITEM_EMPTY(hi)) ! { ! --todo; ! di = HI2DI(hi); ! di->di_flags |= DI_FLAGS_RELOAD; ! } ! // imports can be redefined once ! mark_imports_for_reload(sid); ! // reset version, "vim9script" may have been added or removed. ! si->sn_version = 1; } else { ! // It's new, generate a new SID. ! current_sctx.sc_sid = ++last_current_SID; ! if (ga_grow(&script_items, ! (int)(current_sctx.sc_sid - script_items.ga_len)) == FAIL) ! goto almosttheend; ! while (script_items.ga_len < current_sctx.sc_sid) ! { ! si = ALLOC_CLEAR_ONE(scriptitem_T); ! if (si == NULL) ! goto almosttheend; ! ++script_items.ga_len; ! SCRIPT_ITEM(script_items.ga_len) = si; ! si->sn_name = NULL; ! si->sn_version = 1; ! // Allocate the local script variables to use for this script. ! new_script_vars(script_items.ga_len); ! ga_init2(&si->sn_var_vals, sizeof(svar_T), 10); ! hash_init(&si->sn_all_vars.dv_hashtab); ! ga_init2(&si->sn_imports, sizeof(imported_T), 10); ! ga_init2(&si->sn_type_list, sizeof(type_T), 10); ! # ifdef FEAT_PROFILE ! si->sn_prof_on = FALSE; ! # endif ! } si = SCRIPT_ITEM(current_sctx.sc_sid); si->sn_name = fname_exp; fname_exp = vim_strsave(si->sn_name); // used for autocmd --- 1417,1460 ---- dictitem_T *di; // loading the same script again current_sctx.sc_sid = sid; + si = SCRIPT_ITEM(sid); + if (si->sn_state == SN_STATE_NOT_LOADED) + { + // this script was found but not loaded yet + si->sn_state = SN_STATE_NEW; + } + else + { + si->sn_state = SN_STATE_RELOAD; ! // Script-local variables remain but "const" can be set again. ! // In Vim9 script variables will be cleared when "vim9script" is ! // encountered without the "noclear" argument. ! ht = &SCRIPT_VARS(sid); ! todo = (int)ht->ht_used; ! for (hi = ht->ht_array; todo > 0; ++hi) ! if (!HASHITEM_EMPTY(hi)) ! { ! --todo; ! di = HI2DI(hi); ! di->di_flags |= DI_FLAGS_RELOAD; ! } ! // imports can be redefined once ! mark_imports_for_reload(sid); ! // reset version, "vim9script" may have been added or removed. ! si->sn_version = 1; ! } } else { ! int error = OK; ! // It's new, generate a new SID and initialize the scriptitem. ! current_sctx.sc_sid = get_new_scriptitem(&error); ! if (error == FAIL) ! goto almosttheend; si = SCRIPT_ITEM(current_sctx.sc_sid); si->sn_name = fname_exp; fname_exp = vim_strsave(si->sn_name); // used for autocmd *************** *** 1374,1382 **** // Remember the "is_vimrc" flag for when the file is sourced again. si->sn_is_vimrc = is_vimrc; - - // Used to check script variable index is still valid. - si->sn_script_seq = current_sctx.sc_seq; } # ifdef FEAT_PROFILE --- 1463,1468 ---- *************** *** 2031,2036 **** --- 2117,2194 ---- } /* + * Find the path of a script below the "autoload" directory. + * Returns NULL if there is no "/autoload/" in the script name. + */ + char_u * + script_name_after_autoload(scriptitem_T *si) + { + char_u *p = si->sn_name; + char_u *res = NULL; + + for (;;) + { + char_u *n = (char_u *)strstr((char *)p, "autoload"); + + if (n == NULL) + break; + if (n > p && vim_ispathsep(n[-1]) && vim_ispathsep(n[8])) + res = n + 9; + p = n + 8; + } + return res; + } + + /* + * 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. + */ + char_u * + may_prefix_autoload(char_u *name) + { + if (SCRIPT_ID_VALID(current_sctx.sc_sid)) + { + scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid); + + if (si->sn_is_autoload) + { + char_u *p = script_name_after_autoload(si); + + if (p != NULL) + { + char_u *tail = vim_strsave(p); + + 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); + + 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); + } + } + } + return name; + } + + /* * Return the autoload script name for a function or variable name. * Returns NULL when out of memory. * Caller must make sure that "name" contains AUTOLOAD_CHAR. *** ../vim-8.2.4049/src/proto/scriptfile.pro 2020-09-10 18:25:01.612194701 +0100 --- src/proto/scriptfile.pro 2022-01-09 19:05:13.911795307 +0000 *************** *** 10,15 **** --- 10,16 ---- int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie); int source_runtime(char_u *name, int flags); int source_in_path(char_u *path, char_u *name, int flags, int *ret_sid); + int find_script_in_rtp(char_u *name); void add_pack_start_dirs(void); void load_start_packages(void); void ex_packloadall(exarg_T *eap); *************** *** 36,41 **** --- 37,44 ---- 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 *may_prefix_autoload(char_u *name); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); /* vim: set ft=c : */ *** ../vim-8.2.4049/src/userfunc.c 2022-01-08 16:19:18.509639849 +0000 --- src/userfunc.c 2022-01-09 20:40:16.825423767 +0000 *************** *** 1608,1614 **** p = name + 2; len -= 2; } ! import = find_imported(p, len, NULL); // imported function from another script if (import != NULL) --- 1608,1614 ---- p = name + 2; len -= 2; } ! import = find_imported(p, len, FALSE, NULL); // imported function from another script if (import != NULL) *************** *** 4079,4084 **** --- 4079,4087 ---- else eap->skip = TRUE; } + + // if (is_export) + // name = may_prefix_autoload(name); } // An error in a function call during evaluation of an expression in magic *************** *** 4363,4369 **** { char_u *uname = untrans_function_name(name); ! import = find_imported(uname == NULL ? name : uname, 0, NULL); } if (fp != NULL || import != NULL) --- 4366,4372 ---- { char_u *uname = untrans_function_name(name); ! import = find_imported(uname == NULL ? name : uname, 0, FALSE, NULL); } if (fp != NULL || import != NULL) *** ../vim-8.2.4049/src/eval.c 2022-01-08 16:19:18.501639918 +0000 --- src/eval.c 2022-01-09 19:51:02.672126801 +0000 *************** *** 886,892 **** if (*p == '.' && in_vim9script()) { ! imported_T *import = find_imported(lp->ll_name, p - lp->ll_name, NULL); if (import != NULL) { ufunc_T *ufunc; --- 886,894 ---- if (*p == '.' && in_vim9script()) { ! imported_T *import = find_imported(lp->ll_name, p - lp->ll_name, ! TRUE, NULL); ! if (import != NULL) { ufunc_T *ufunc; *** ../vim-8.2.4049/src/evalvars.c 2022-01-08 16:19:18.501639918 +0000 --- src/evalvars.c 2022-01-09 19:52:11.080080967 +0000 *************** *** 2683,2689 **** char_u *p = STRNCMP(name, "s:", 2) == 0 ? name + 2 : name; if (sid == 0) ! import = find_imported(p, 0, NULL); // imported variable from another script if (import != NULL || sid != 0) --- 2683,2689 ---- char_u *p = STRNCMP(name, "s:", 2) == 0 ? name + 2 : name; if (sid == 0) ! import = find_imported(p, 0, TRUE, NULL); // imported variable from another script if (import != NULL || sid != 0) *************** *** 3015,3021 **** res = HASHITEM_EMPTY(hi) ? FAIL : OK; // if not script-local, then perhaps imported ! if (res == FAIL && find_imported(p, 0, NULL) != NULL) res = OK; if (p != buffer) vim_free(p); --- 3015,3021 ---- res = HASHITEM_EMPTY(hi) ? FAIL : OK; // if not script-local, then perhaps imported ! if (res == FAIL && find_imported(p, 0, FALSE, NULL) != NULL) res = OK; if (p != buffer) vim_free(p); *************** *** 3388,3394 **** if (di == NULL && var_in_vim9script) { ! imported_T *import = find_imported(varname, 0, NULL); if (import != NULL) { --- 3388,3394 ---- if (di == NULL && var_in_vim9script) { ! imported_T *import = find_imported(varname, 0, FALSE, NULL); if (import != NULL) { *** ../vim-8.2.4049/src/vim9compile.c 2022-01-08 18:43:36.877446896 +0000 --- src/vim9compile.c 2022-01-09 20:31:53.506442778 +0000 *************** *** 268,274 **** && (lookup_local(name, len, NULL, cctx) == OK || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)) || script_var_exists(name, len, cctx) == OK ! || find_imported(name, len, cctx) != NULL; } /* --- 268,274 ---- && (lookup_local(name, len, NULL, cctx) == OK || arg_exists(name, len, NULL, NULL, NULL, cctx) == OK)) || script_var_exists(name, len, cctx) == OK ! || find_imported(name, len, FALSE, cctx) != NULL; } /* *************** *** 331,337 **** if ((cctx != NULL && (lookup_local(p, len, NULL, cctx) == OK || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) ! || find_imported(p, len, cctx) != NULL || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) { // A local or script-local function can shadow a global function. --- 331,337 ---- if ((cctx != NULL && (lookup_local(p, len, NULL, cctx) == OK || arg_exists(p, len, NULL, NULL, NULL, cctx) == OK)) ! || find_imported(p, len, FALSE, cctx) != NULL || (ufunc = find_func_even_dead(p, FALSE, cctx)) != NULL) { // A local or script-local function can shadow a global function. *************** *** 581,591 **** /* * Find "name" in imported items of the current script or in "cctx" if not * NULL. */ imported_T * ! find_imported(char_u *name, size_t len, cctx_T *cctx) { int idx; if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) return NULL; --- 581,593 ---- /* * Find "name" in imported items of the current script or in "cctx" if not * NULL. + * If "load" is TRUE and the script was not loaded yet, load it now. */ imported_T * ! find_imported(char_u *name, size_t len, int load, cctx_T *cctx) { int idx; + imported_T *ret = NULL; if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) return NULL; *************** *** 598,607 **** if (len == 0 ? STRCMP(name, import->imp_name) == 0 : STRLEN(import->imp_name) == len && STRNCMP(name, import->imp_name, len) == 0) ! return import; } ! return find_imported_in_script(name, len, current_sctx.sc_sid); } /* --- 600,622 ---- if (len == 0 ? STRCMP(name, import->imp_name) == 0 : STRLEN(import->imp_name) == len && STRNCMP(name, import->imp_name, len) == 0) ! { ! ret = import; ! break; ! } } ! if (ret == NULL) ! ret = find_imported_in_script(name, len, current_sctx.sc_sid); ! ! if (ret != NULL && load && ret->imp_flags == IMP_FLAGS_AUTOLOAD) ! { ! // script found before but not loaded yet ! ret->imp_flags = 0; ! (void)do_source(SCRIPT_ITEM(ret->imp_sid)->sn_name, FALSE, ! DOSO_NONE, NULL); ! } ! return ret; } /* *************** *** 1326,1332 **** : script_var_exists(var_start, lhs->lhs_varlen, cctx)) == OK; imported_T *import = ! find_imported(var_start, lhs->lhs_varlen, cctx); if (script_namespace || script_var || import != NULL) { --- 1341,1347 ---- : script_var_exists(var_start, lhs->lhs_varlen, cctx)) == OK; imported_T *import = ! find_imported(var_start, lhs->lhs_varlen, FALSE, cctx); if (script_namespace || script_var || import != NULL) { *** ../vim-8.2.4049/src/proto/vim9compile.pro 2022-01-08 18:43:36.877446896 +0000 --- src/proto/vim9compile.pro 2022-01-09 19:50:12.916155722 +0000 *************** *** 7,13 **** int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type); int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx); ! imported_T *find_imported(char_u *name, size_t len, cctx_T *cctx); char_u *may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp); char_u *peek_next_line_from_context(cctx_T *cctx); char_u *next_line_from_context(cctx_T *cctx, int skip_comment); --- 7,13 ---- int need_type(type_T *actual, type_T *expected, int offset, int arg_idx, cctx_T *cctx, int silent, int actual_is_const); lvar_T *reserve_local(cctx_T *cctx, char_u *name, size_t len, int isConst, type_T *type); int get_script_item_idx(int sid, char_u *name, int check_writable, cctx_T *cctx); ! imported_T *find_imported(char_u *name, size_t len, int load, cctx_T *cctx); char_u *may_peek_next_line(cctx_T *cctx, char_u *arg, char_u **nextp); char_u *peek_next_line_from_context(cctx_T *cctx); char_u *next_line_from_context(cctx_T *cctx, int skip_comment); *** ../vim-8.2.4049/src/vim9expr.c 2022-01-09 13:36:20.351866599 +0000 --- src/vim9expr.c 2022-01-09 19:56:21.231865382 +0000 *************** *** 266,272 **** return OK; } ! import = end == NULL ? NULL : find_imported(name, 0, cctx); if (import != NULL) { char_u *p = skipwhite(*end); --- 266,272 ---- return OK; } ! import = end == NULL ? NULL : find_imported(name, 0, FALSE, cctx); if (import != NULL) { char_u *p = skipwhite(*end); *************** *** 275,280 **** --- 275,281 ---- ufunc_T *ufunc; type_T *type; + // TODO: if this is an autoload import do something else. // Need to lookup the member. if (*p != '.') { *************** *** 474,480 **** // "var" can be script-local even without using "s:" if it // already exists in a Vim9 script or when it's imported. if (script_var_exists(*arg, len, cctx) == OK ! || find_imported(name, 0, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); // When evaluating an expression and the name starts with an --- 475,481 ---- // "var" can be script-local even without using "s:" if it // already exists in a Vim9 script or when it's imported. if (script_var_exists(*arg, len, cctx) == OK ! || find_imported(name, 0, FALSE, cctx) != NULL) res = compile_load_scriptvar(cctx, name, *arg, &end, FALSE); // When evaluating an expression and the name starts with an *** ../vim-8.2.4049/src/testdir/test_vim9_script.vim 2022-01-07 21:38:43.171435890 +0000 --- src/testdir/test_vim9_script.vim 2022-01-09 20:43:06.881446211 +0000 *************** *** 3032,3037 **** --- 3032,3057 ---- writefile(lines, 'Xdir/autoload/Other.vim') assert_equal('other', g:Other#getOther()) + # using "vim9script autoload" prefix is not needed + lines =<< trim END + vim9script autoload + g:prefixed_loaded = 'yes' + export def Gettest(): string + return 'test' + enddef + export var name = 'name' + END + writefile(lines, 'Xdir/autoload/prefixed.vim') + + lines =<< trim END + vim9script + import autoload 'prefixed.vim' + assert_false(exists('g:prefixed_loaded')) + assert_equal('test', prefixed.Gettest()) + assert_equal('yes', g:prefixed_loaded) + END + CheckScriptSuccess(lines) + delete('Xdir', 'rf') &rtp = save_rtp enddef *** ../vim-8.2.4049/src/version.c 2022-01-09 13:36:20.351866599 +0000 --- src/version.c 2022-01-09 21:29:00.618164671 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4050, /**/ -- ARTHUR: This new learning amazes me, Sir Bedevere. Explain again how sheep's bladders may be employed to prevent earthquakes. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///