1
0
forked from aniani/vim

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".
This commit is contained in:
Bram Moolenaar
2022-01-09 21:36:37 +00:00
parent 5f25c38550
commit dc4451df61
15 changed files with 402 additions and 117 deletions

View File

@@ -239,6 +239,102 @@ source_callback(char_u *fname, void *cookie)
(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,9 +560,23 @@ source_in_path(char_u *path, char_u *name, int flags, int *ret_sid)
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.
*/
@@ -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)
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;
int error = OK;
// 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
}
// 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,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
@@ -2030,6 +2116,78 @@ source_finished(
fgetline, cookie))->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.