changeset 28447:6f753a8125f0 v8.2.4748

patch 8.2.4748: cannot use an imported function in a mapping Commit: https://github.com/vim/vim/commit/8944551534b311a2d25acf6e8db235c6d906256c Author: Bram Moolenaar <Bram@vim.org> Date: Thu Apr 14 12:58:23 2022 +0100 patch 8.2.4748: cannot use an imported function in a mapping Problem: Cannot use an imported function in a mapping. Solution: Recognize <SID>name.Func.
author Bram Moolenaar <Bram@vim.org>
date Thu, 14 Apr 2022 14:00:05 +0200
parents 224455817fac
children 68c4651c8dfc
files runtime/doc/vim9.txt src/proto/vim9execute.pro src/scriptfile.c src/term.c src/testdir/test_vim9_import.vim src/version.c src/vim9execute.c
diffstat 7 files changed, 105 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -1720,7 +1720,15 @@ line, there can be no line break: >
 		name   # Error!
 	echo that
 		.name  # Error!
-<							*:import-cycle*
+
+To refer to a function in an imported script in a mapping, |<SID>| can be
+used: >
+	noremap <silent> ,a :call <SID>name.Function()<CR>
+
+When the mapping is defined "<SID>name." will be replaced with <SNR> and the
+script ID of the imported script.
+
+							*:import-cycle*
 The `import` commands are executed when encountered.  If script A imports
 script B, and B (directly or indirectly) imports A, this will be skipped over.
 At this point items in A after "import B" will not have been processed and
--- a/src/proto/vim9execute.pro
+++ b/src/proto/vim9execute.pro
@@ -6,6 +6,7 @@ int set_ref_in_funcstacks(int copyID);
 char_u *char_from_string(char_u *str, varnumber_T index);
 char_u *string_slice(char_u *str, varnumber_T first, varnumber_T last, int exclusive);
 int fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx);
+int may_load_script(int sid, int *loaded);
 typval_T *lookup_debug_var(char_u *name);
 int may_break_in_function(ufunc_T *ufunc);
 int exe_typval_instr(typval_T *tv, typval_T *rettv);
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -117,7 +117,7 @@ estack_pop(void)
 }
 
 /*
- * Get the current value for <sfile> in allocated memory.
+ * Get the current value for "which" in allocated memory.
  * "which" is ESTACK_SFILE for <sfile>, ESTACK_STACK for <stack> or
  * ESTACK_SCRIPT for <script>.
  */
@@ -2468,6 +2468,18 @@ script_autoload(
     int		i;
     int		ret_sid;
 
+    // If the name starts with "<SNR>123_" then "123" is the script ID.
+    if (name[0] == K_SPECIAL && name[1] == KS_EXTRA && name[2] == KE_SNR)
+    {
+	p = name + 3;
+	ret_sid = (int)getdigits(&p);
+	if (*p == '_' && SCRIPT_ID_VALID(ret_sid))
+	{
+	    may_load_script(ret_sid, &ret);
+	    return ret;
+	}
+    }
+
     // If there is no '#' after name[0] there is no package name.
     p = vim_strchr(name, AUTOLOAD_CHAR);
     if (p == NULL || p == name)
--- a/src/term.c
+++ b/src/term.c
@@ -6010,8 +6010,11 @@ replace_termcodes(
 	{
 #ifdef FEAT_EVAL
 	    /*
-	     * Replace <SID> by K_SNR <script-nr> _.
+	     * Change <SID>Func to K_SNR <script-nr> _Func.  This name is used
+	     * for script-locla user functions.
 	     * (room: 5 * 6 = 30 bytes; needed: 3 + <nr> + 1 <= 14)
+	     * Also change <SID>name.Func to K_SNR <import-script-nr> _Func.
+	     * Only if "name" is recognized as an import.
 	     */
 	    if (STRNICMP(src, "<SID>", 5) == 0)
 	    {
@@ -6019,12 +6022,26 @@ replace_termcodes(
 		    emsg(_(e_using_sid_not_in_script_context));
 		else
 		{
+		    char_u  *dot;
+		    long    sid = current_sctx.sc_sid;
+
 		    src += 5;
+		    if (in_vim9script()
+				       && (dot = vim_strchr(src, '.')) != NULL)
+		    {
+			imported_T *imp = find_imported(src, dot - src, FALSE);
+
+			if (imp != NULL)
+			{
+			    sid = imp->imp_sid;
+			    src = dot + 1;
+			}
+		    }
+
 		    result[dlen++] = K_SPECIAL;
 		    result[dlen++] = (int)KS_EXTRA;
 		    result[dlen++] = (int)KE_SNR;
-		    sprintf((char *)result + dlen, "%ld",
-						    (long)current_sctx.sc_sid);
+		    sprintf((char *)result + dlen, "%ld", sid);
 		    dlen += (int)STRLEN(result + dlen);
 		    result[dlen++] = '_';
 		    continue;
--- a/src/testdir/test_vim9_import.vim
+++ b/src/testdir/test_vim9_import.vim
@@ -642,8 +642,8 @@ enddef
 def Test_use_import_in_mapping()
   var lines =<< trim END
       vim9script
-      export def Funcx()
-        g:result = 42
+      export def Funcx(nr: number)
+        g:result = nr
       enddef
   END
   writefile(lines, 'XsomeExport.vim')
@@ -651,18 +651,48 @@ def Test_use_import_in_mapping()
       vim9script
       import './XsomeExport.vim' as some
       var Funcy = some.Funcx
-      nnoremap <F3> :call <sid>Funcy()<cr>
+      nnoremap <F3> :call <sid>Funcy(42)<cr>
+      nnoremap <F4> :call <sid>some.Funcx(44)<cr>
   END
   writefile(lines, 'Xmapscript.vim')
 
   source Xmapscript.vim
   feedkeys("\<F3>", "xt")
   assert_equal(42, g:result)
+  feedkeys("\<F4>", "xt")
+  assert_equal(44, g:result)
 
   unlet g:result
   delete('XsomeExport.vim')
   delete('Xmapscript.vim')
   nunmap <F3>
+  nunmap <F4>
+enddef
+
+def Test_use_autoload_import_in_mapping()
+  var lines =<< trim END
+      vim9script
+      export def Func()
+        g:result = 42
+      enddef
+  END
+  writefile(lines, 'XautoloadExport.vim')
+  lines =<< trim END
+      vim9script
+      import autoload './XautoloadExport.vim' as some
+      nnoremap <F3> :call <SID>some.Func()<CR>
+  END
+  writefile(lines, 'Xmapscript.vim')
+
+  source Xmapscript.vim
+  assert_match('\d\+ A: .*XautoloadExport.vim', execute('scriptnames')->split("\n")[-1])
+  feedkeys("\<F3>", "xt")
+  assert_equal(42, g:result)
+
+  unlet g:result
+  delete('XautoloadExport.vim')
+  delete('Xmapscript.vim')
+  nunmap <F3>
 enddef
 
 def Test_use_import_in_command_completion()
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4748,
+/**/
     4747,
 /**/
     4746,
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -1658,6 +1658,29 @@ exec_command(isn_T *iptr)
     return OK;
 }
 
+/*
+ * If script "sid" is not loaded yet then load it now.
+ * Caller must make sure "sid" is a valid script ID.
+ * "loaded" is set to TRUE if the script had to be loaded.
+ * Returns FAIL if loading fails, OK if already loaded or loaded now.
+ */
+    int
+may_load_script(int sid, int *loaded)
+{
+    scriptitem_T *si = SCRIPT_ITEM(sid);
+
+    if (si->sn_state == SN_STATE_NOT_LOADED)
+    {
+	*loaded = TRUE;
+	if (do_source(si->sn_name, FALSE, DOSO_NONE, NULL) == FAIL)
+	{
+	    semsg(_(e_cant_open_file_str), si->sn_name);
+	    return FAIL;
+	}
+    }
+    return OK;
+}
+
 // used for v_instr of typval of VAR_INSTR
 struct instr_S {
     ectx_T	*instr_ectx;
@@ -2632,18 +2655,11 @@ exec_instructions(ectx_T *ectx)
 
 	    case ISN_SOURCE:
 		{
-		    scriptitem_T *si = SCRIPT_ITEM(iptr->isn_arg.number);
-
-		    if (si->sn_state == SN_STATE_NOT_LOADED)
-		    {
-			SOURCING_LNUM = iptr->isn_lnum;
-			if (do_source(si->sn_name, FALSE, DOSO_NONE, NULL)
+		    int notused;
+		    SOURCING_LNUM = iptr->isn_lnum;
+		    if (may_load_script((int)iptr->isn_arg.number, &notused)
 								       == FAIL)
-			{
-			    semsg(_(e_cant_open_file_str), si->sn_name);
-			    goto on_error;
-			}
-		    }
+			goto on_error;
 		}
 		break;