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:
276
src/scriptfile.c
276
src/scriptfile.c
@@ -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.
|
||||
|
Reference in New Issue
Block a user