Patch 8.2.4050

19 views
Skip to first unread message

Bram Moolenaar

unread,
Jan 9, 2022, 4:37:37 PM1/9/22
to vim...@googlegroups.com

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(<f-args>)

< 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(<f-args>)

< 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 -- Br...@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 ///

John Marriott

unread,
Jan 9, 2022, 8:29:41 PM1/9/22
to vim...@googlegroups.com

On 10-Jan-2022 08:37, Bram Moolenaar wrote:
> 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
>
>
>
After this patch, mingw64 (gcc 11.2.0) comes up with this warning:
<snip>
gcc -c -I. -Iproto -DWIN32 -DWINVER=0x0603 -D_WIN32_WINNT=0x0603
-DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_STDIO
-pipe -march=native -Wall -O3 -fomit-frame-pointer -freg-struct-return
-fpie -fPIE -DFEAT_GUI_MSWIN -DFEAT_CLIPBOARD scriptfile.c -o
gobjnative/scriptfile.o
scriptfile.c: In function 'get_new_scriptitem':
scriptfile.c:309:23: warning: 'si' may be used uninitialized in this
function [-Wmaybe-uninitialized]
  309 |     si->sn_script_seq = current_sctx.sc_seq;
      |     ~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~
</snip>

The attached patch tries to fix it.

Cheers
John
scriptfile.c.8.2.4050.patch

Maxim Kim

unread,
Jan 10, 2022, 2:16:24 AM1/10/22
to vim_dev
I have tried new way of autoload and for me it didn't work (windows 8.2.4050)

I have autoload/comment.vim:

vim9script autoload

# Toggle comments
# Usage:
#   1. Save in ~/.vim/autoload/comment.vim
#   2. Add following mappings to vimrc:
#      import autoload 'comment.vim'
#      nnoremap <silent> <expr> gc comment.Toggle()
#      xnoremap <silent> <expr> gc comment.Toggle()
#      nnoremap <silent> <expr> gcc comment.Toggle() .. '_'
export def Toggle(...args: list<string>): string
...

And in my ~/vimfiles/vimrc:

import autoload 'comment.vim'
nnoremap <silent> <expr> gc comment.Toggle()
xnoremap <silent> <expr> gc comment.Toggle()
nnoremap <silent> <expr> gcc comment.Toggle() .. '_'
# nnoremap <silent> <expr> gc comment#toggle()
# xnoremap <silent> <expr> gc comment#toggle()
# nnoremap <silent> <expr> gcc comment#toggle() .. '_'

After pressing gcc I get error:

E121: Undefined variable: comment

2022-01-10_10-17-06.png

Bram Moolenaar

unread,
Jan 10, 2022, 7:17:38 AM1/10/22
to vim...@googlegroups.com, John Marriott

John Marriott wrote:

> On 10-Jan-2022 08:37, Bram Moolenaar wrote:
> > 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
> >
> >
> >
> After this patch, mingw64 (gcc 11.2.0) comes up with this warning:
> <snip>
> gcc -c -I. -Iproto -DWIN32 -DWINVER=0x0603 -D_WIN32_WINNT=0x0603
> -DHAVE_PATHDEF -DFEAT_NORMAL -DHAVE_STDINT_H -D__USE_MINGW_ANSI_STDIO
> -pipe -march=native -Wall -O3 -fomit-frame-pointer -freg-struct-return
> -fpie -fPIE -DFEAT_GUI_MSWIN -DFEAT_CLIPBOARD scriptfile.c -o
> gobjnative/scriptfile.o
> scriptfile.c: In function 'get_new_scriptitem':
> scriptfile.c:309:23: warning: 'si' may be used uninitialized in this
> function [-Wmaybe-uninitialized]
>   309 |     si->sn_script_seq = current_sctx.sc> _seq;
>       |     ~~~~~~~~~~~~~~~~~> ~^~~~~~~~~~~~~~~~~~~~~
> </snip>
>
> The attached patch tries to fix it.

Yeah, "si" can't really be NULL but the compiler doesn't know that.
Thanks for the patch.

Still more work to do on this autoload mechanism...

--
This sentence is not sure that it exists, but if it does, it will
certainly consider the possibility that other sentences exist.

Bram Moolenaar

unread,
Jan 10, 2022, 7:17:40 AM1/10/22
to vim...@googlegroups.com, Maxim Kim
This isn't fully implemented yet. It turns out it's more complicated.

Thanks for the example, I'll try it out after some more changes.

--
GALAHAD: Camelot ...
LAUNCELOT: Camelot ...
GAWAIN: It's only a model.
"Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

Maxim Kim

unread,
Jan 11, 2022, 1:31:54 AM1/11/22
to vim_dev
Following:

import autoload 'comment.vim'
nnoremap <silent> <expr> gc comment.Toggle()
xnoremap <silent> <expr> gc comment.Toggle()
nnoremap <silent> <expr> gcc comment.Toggle() .. '_'

Now works (as of 8.2.4058)
понедельник, 10 января 2022 г. в 15:17:40 UTC+3, Bram Moolenaar:

Maxim Kim

unread,
Jan 11, 2022, 1:33:26 AM1/11/22
to vim_dev
Oops, I take it back -- it doesn't work :)

вторник, 11 января 2022 г. в 09:31:54 UTC+3, Maxim Kim:
Reply all
Reply to author
Forward
0 new messages