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

@ -365,11 +365,12 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
Vim version, or update Vim to a newer version. See
|vimscript-version| for what changed between versions.
:vim9s[cript] [noclear] *:vim9s* *:vim9script*
: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

View File

@ -1501,37 +1501,43 @@ result in undefined items.
Import in an autoload script ~
*vim9-autoload*
For optimal startup speed, loading scripts should be postponed until they are
actually needed. A recommended mechanism:
actually needed. Using the autoload mechanism is recommended:
1. In the plugin define user commands, functions and/or mappings that refer to
an autoload script. >
command -nargs=1 SearchForStuff searchfor#Stuff(<f-args>)
items imported from an autoload script. >
import autoload 'for/search.vim'
command -nargs=1 SearchForStuff search.Stuff(<f-args>)
< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen.
The "SearchForStuff" command is now available to the user.
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)
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/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.
< 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 ~

View File

@ -3203,4 +3203,8 @@ EXTERN char e_cannot_import_dot_vim_without_using_as[]
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

View File

@ -886,7 +886,9 @@ get_lval(
if (*p == '.' && in_vim9script())
{
imported_T *import = find_imported(lp->ll_name, p - lp->ll_name, NULL);
imported_T *import = find_imported(lp->ll_name, p - lp->ll_name,
TRUE, NULL);
if (import != NULL)
{
ufunc_T *ufunc;

View File

@ -2683,7 +2683,7 @@ eval_variable(
char_u *p = STRNCMP(name, "s:", 2) == 0 ? name + 2 : name;
if (sid == 0)
import = find_imported(p, 0, NULL);
import = find_imported(p, 0, TRUE, NULL);
// imported variable from another script
if (import != NULL || sid != 0)
@ -3015,7 +3015,7 @@ lookup_scriptitem(
res = HASHITEM_EMPTY(hi) ? FAIL : OK;
// if not script-local, then perhaps imported
if (res == FAIL && find_imported(p, 0, NULL) != NULL)
if (res == FAIL && find_imported(p, 0, FALSE, NULL) != NULL)
res = OK;
if (p != buffer)
vim_free(p);
@ -3388,7 +3388,7 @@ set_var_const(
if (di == NULL && var_in_vim9script)
{
imported_T *import = find_imported(varname, 0, NULL);
imported_T *import = find_imported(varname, 0, FALSE, NULL);
if (import != NULL)
{

View File

@ -10,6 +10,7 @@ int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *f
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,6 +37,8 @@ void ex_scriptversion(exarg_T *eap);
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 : */

View File

@ -7,7 +7,7 @@ int check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg);
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);
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);

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.

View File

@ -1827,6 +1827,7 @@ typedef struct {
} 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,6 +1864,7 @@ 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"
# ifdef FEAT_PROFILE
int sn_prof_on; // TRUE when script is/was profiled
@ -1886,7 +1888,8 @@ typedef struct
} 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_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.

View File

@ -3032,6 +3032,26 @@ def Test_vim9_autoload()
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

View File

@ -1608,7 +1608,7 @@ deref_func_name(
p = name + 2;
len -= 2;
}
import = find_imported(p, len, NULL);
import = find_imported(p, len, FALSE, NULL);
// imported function from another script
if (import != NULL)
@ -4079,6 +4079,9 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
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,7 +4366,7 @@ define_function(exarg_T *eap, char_u *name_arg, garray_T *lines_to_free)
{
char_u *uname = untrans_function_name(name);
import = find_imported(uname == NULL ? name : uname, 0, NULL);
import = find_imported(uname == NULL ? name : uname, 0, FALSE, NULL);
}
if (fp != NULL || import != NULL)

View File

@ -750,6 +750,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
4050,
/**/
4049,
/**/

View File

@ -268,7 +268,7 @@ variable_exists(char_u *name, size_t len, cctx_T *cctx)
&& (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;
|| find_imported(name, len, FALSE, cctx) != NULL;
}
/*
@ -331,7 +331,7 @@ check_defined(char_u *p, size_t len, cctx_T *cctx, int is_arg)
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
|| 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,11 +581,13 @@ find_imported_in_script(char_u *name, size_t len, int sid)
/*
* 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, cctx_T *cctx)
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,10 +600,23 @@ find_imported(char_u *name, size_t len, cctx_T *cctx)
if (len == 0 ? STRCMP(name, import->imp_name) == 0
: STRLEN(import->imp_name) == len
&& STRNCMP(name, import->imp_name, len) == 0)
return import;
{
ret = import;
break;
}
}
return find_imported_in_script(name, len, current_sctx.sc_sid);
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,7 +1341,7 @@ compile_lhs(
: script_var_exists(var_start, lhs->lhs_varlen,
cctx)) == OK;
imported_T *import =
find_imported(var_start, lhs->lhs_varlen, cctx);
find_imported(var_start, lhs->lhs_varlen, FALSE, cctx);
if (script_namespace || script_var || import != NULL)
{

View File

@ -266,7 +266,7 @@ compile_load_scriptvar(
return OK;
}
import = end == NULL ? NULL : find_imported(name, 0, cctx);
import = end == NULL ? NULL : find_imported(name, 0, FALSE, cctx);
if (import != NULL)
{
char_u *p = skipwhite(*end);
@ -275,6 +275,7 @@ compile_load_scriptvar(
ufunc_T *ufunc;
type_T *type;
// TODO: if this is an autoload import do something else.
// Need to lookup the member.
if (*p != '.')
{
@ -474,7 +475,7 @@ compile_load(
// "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)
|| 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

View File

@ -68,6 +68,9 @@ ex_vim9script(exarg_T *eap UNUSED)
#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,12 +84,40 @@ ex_vim9script(exarg_T *eap UNUSED)
emsg(_(e_vim9script_must_be_first_command_in_script));
return;
}
if (!IS_WHITE_OR_NUL(*eap->arg) && STRCMP(eap->arg, "noclear") != 0)
for (p = eap->arg; !IS_WHITE_OR_NUL(*p); p = skipwhite(skiptowhite(p)))
{
semsg(_(e_invalid_argument_str), eap->arg);
return;
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 && IS_WHITE_OR_NUL(*eap->arg))
if (si->sn_state == SN_STATE_RELOAD && !found_noclear)
{
hashtab_T *ht = &SCRIPT_VARS(sid);
@ -101,6 +132,8 @@ ex_vim9script(exarg_T *eap UNUSED)
}
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,6 +399,7 @@ handle_import(
{
char_u *arg = arg_start;
char_u *nextarg;
int is_autoload = FALSE;
int getnext;
char_u *expr_end;
int ret = FAIL;
@ -377,6 +411,12 @@ handle_import(
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,23 +442,48 @@ handle_import(
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);
if (is_autoload)
res = FAIL;
else
{
res = do_source(from_name, FALSE, DOSO_NONE, &sid);
vim_free(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);
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,9 +493,7 @@ handle_import(
// 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);
@ -438,7 +501,9 @@ handle_import(
if (res == FAIL || sid <= 0)
{
semsg(_(e_could_not_import_str), tv.vval.v_string);
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,7 +516,7 @@ handle_import(
{
if (import->imp_flags & IMP_FLAGS_RELOAD)
{
// encountering same script first ime on a reload is OK
// encountering same script first time on a reload is OK
import->imp_flags &= ~IMP_FLAGS_RELOAD;
break;
}
@ -513,7 +578,7 @@ handle_import(
{
imported_T *imported;
imported = find_imported(as_name, STRLEN(as_name), cctx);
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,6 +594,8 @@ handle_import(
imported->imp_name = as_name;
as_name = NULL;
imported->imp_sid = sid;
if (is_autoload)
imported->imp_flags = IMP_FLAGS_AUTOLOAD;
}
erret: