Mercurial > vim
diff src/scriptfile.c @ 27043:15f40772e10a v8.2.4050
patch 8.2.4050: Vim9: need to prefix every item in an autoload script
Commit: https://github.com/vim/vim/commit/dc4451df61a6aa12a0661817b7094fb32f09e11d
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun Jan 9 21:36:37 2022 +0000
patch 8.2.4050: Vim9: need to prefix every item in an autoload script
Problem: Vim9: need to prefix every item in an autoload script.
Solution: First step in supporting "vim9script autoload" and "import
autoload".
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 09 Jan 2022 22:45:04 +0100 |
parents | c9474ae175f4 |
children | d31bd8607975 |
line wrap: on
line diff
--- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -239,6 +239,102 @@ source_callback(char_u *fname, void *coo (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,7 +551,8 @@ source_runtime(char_u *name, int flags) } /* - * Just like source_runtime(), but use "path" instead of 'runtimepath'. + * 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,8 +560,22 @@ source_in_path(char_u *path, char_u *nam return do_in_path_and_pp(path, name, flags, source_callback, ret_sid); } +#if defined(FEAT_EVAL) || defined(PROTO) -#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. @@ -1127,7 +1238,6 @@ do_source( 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; @@ -1161,18 +1271,7 @@ do_source( 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; - } + sid = find_script_by_name(fname_exp); if (sid > 0 && ret_sid != NULL) { // Already loaded and no need to load again, return here. @@ -1318,54 +1417,44 @@ do_source( dictitem_T *di; // loading the same script again - si->sn_state = SN_STATE_RELOAD; 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); + // 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; + // 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) + 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; - 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 @@ -1374,9 +1463,6 @@ do_source( // 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 @@ -2031,6 +2117,78 @@ source_finished( } /* + * 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.