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.