]> granicus.if.org Git - vim/commitdiff
patch 8.2.4748: cannot use an imported function in a mapping v8.2.4748
authorBram Moolenaar <Bram@vim.org>
Thu, 14 Apr 2022 11:58:23 +0000 (12:58 +0100)
committerBram Moolenaar <Bram@vim.org>
Thu, 14 Apr 2022 11:58:23 +0000 (12:58 +0100)
Problem:    Cannot use an imported function in a mapping.
Solution:   Recognize <SID>name.Func.

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

index 5020bcf90b7286638ee5040bbac33b81a489211b..b30e5f3d4ad774f267ba055344ded5ed55885cb7 100644 (file)
@@ -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
index bd670929157fa388e231a877375c14e7c0678597..80afb8536a7a52f4cc47dccea85492b2bc6f4d09 100644 (file)
@@ -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);
index 7e14cdc6e351f75e44fe6f934ba0831f70001302..2a1f84a50e6c0a27823027cb3a770f6c283b5a70 100644 (file)
@@ -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)
index 8709cd365dfb65ca7c28acff9991dfe75c6f7c43..e51134f16e36f93cc962d0a6d1970d491db452eb 100644 (file)
@@ -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;
index 2c3792f844d594f4059cd58faf806a053af955f8..94cb76033e1728a85d7281fc818ec02bef3802ed 100644 (file)
@@ -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()
index 9f9917726ff250ea51c9e6281992466f9e95f19d..e1a5bbd661fdb7c6fd75a20ad31dd52276a29803 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4748,
 /**/
     4747,
 /**/
index 0e8a1dd951a2b0c2384bc47a5f0de5b168c1fe59..da2822c1b5de47a7cdd2da667992729ca71e9a31 100644 (file)
@@ -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;