]> granicus.if.org Git - vim/commitdiff
patch 8.2.1124: Vim9: no line break allowed in :import command v8.2.1124
authorBram Moolenaar <Bram@vim.org>
Sat, 4 Jul 2020 11:15:31 +0000 (13:15 +0200)
committerBram Moolenaar <Bram@vim.org>
Sat, 4 Jul 2020 11:15:31 +0000 (13:15 +0200)
Problem:    Vim9: no line break allowed in :import command.
Solution:   Skip over line breaks.

src/proto/vim9script.pro
src/testdir/test_vim9_script.vim
src/version.c
src/vim9compile.c
src/vim9script.c

index a11f6af7a5f30984c497da6bd338f76b7c7f1013..6a32abdcacbc050e59ea88968213b86198a246e6 100644 (file)
@@ -4,8 +4,8 @@ void ex_vim9script(exarg_T *eap);
 void ex_export(exarg_T *eap);
 void free_imports(int sid);
 void ex_import(exarg_T *eap);
-int find_exported(int sid, char_u **argp, int *name_len, ufunc_T **ufunc, type_T **type);
-char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx);
+int find_exported(int sid, char_u *name, ufunc_T **ufunc, type_T **type);
+char_u *handle_import(char_u *arg_start, garray_T *gap, int import_sid, evalarg_T *evalarg, void *cctx);
 char_u *vim9_declare_scriptvar(exarg_T *eap, char_u *arg);
 int check_script_var_type(typval_T *dest, typval_T *value, char_u *name);
 /* vim: set ft=c : */
index f9301ca26b33152e414feb2f9e761f88f678e28b..8411621419516b2f5091f55338ff0eea00a8ff8d 100644 (file)
@@ -686,6 +686,35 @@ def Test_vim9_import_export()
   unlet g:imported_name g:imported_name_appended
   delete('Ximport.vim')
 
+  # similar, with line breaks
+  let import_line_break_script_lines =<< trim END
+    vim9script
+    import {
+        exported,
+        Exported,
+        }
+        from
+        './Xexport.vim'
+    g:imported = exported
+    exported += 5
+    g:imported_added = exported
+    g:imported_func = Exported()
+  END
+  writefile(import_line_break_script_lines, 'Ximport_lbr.vim')
+  source Ximport_lbr.vim
+
+  assert_equal(9876, g:imported)
+  assert_equal(9881, g:imported_added)
+  assert_equal('Exported', g:imported_func)
+
+  # exported script not sourced again
+  assert_false(exists('g:result'))
+  unlet g:imported
+  unlet g:imported_added
+  unlet g:imported_func
+  delete('Ximport_lbr.vim')
+
+  # import inside :def function
   let import_in_def_lines =<< trim END
     vim9script
     def ImportInDef()
@@ -751,6 +780,21 @@ def Test_vim9_import_export()
   writefile(import_star_as_lines_missing_name, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1048:')
 
+  let import_star_as_lbr_lines =<< trim END
+    vim9script
+    import *
+        as Export
+        from
+        './Xexport.vim'
+    def UseExport()
+      g:imported = Export.exported
+    enddef
+    UseExport()
+  END
+  writefile(import_star_as_lbr_lines, 'Ximport.vim')
+  source Ximport.vim
+  assert_equal(9883, g:imported)
+
   let import_star_lines =<< trim END
     vim9script
     import * from './Xexport.vim'
index 6908662bc60df08c28f14c6c7466a590a7041aad..2f4bb6bd557f6974ba39a1c5e1a0bb041187fb8d 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1124,
 /**/
     1123,
 /**/
index bb3122f40d5499c63f4071a7e1abb21a3d4ae9b1..9c2eaf43292347d004bc5c180946e4a653ad00fa 100644 (file)
@@ -2613,7 +2613,8 @@ compile_load_scriptvar(
        if (import->imp_all)
        {
            char_u      *p = skipwhite(*end);
-           int         name_len;
+           char_u      *exp_name;
+           int         cc;
            ufunc_T     *ufunc;
            type_T      *type;
 
@@ -2630,7 +2631,17 @@ compile_load_scriptvar(
                return FAIL;
            }
 
-           idx = find_exported(import->imp_sid, &p, &name_len, &ufunc, &type);
+           // isolate one name
+           exp_name = p;
+           while (eval_isnamec(*p))
+               ++p;
+           cc = *p;
+           *p = NUL;
+
+           idx = find_exported(import->imp_sid, exp_name, &ufunc, &type);
+           *p = cc;
+           p = skipwhite(p);
+
            // TODO: what if it is a function?
            if (idx < 0)
                return FAIL;
@@ -2981,6 +2992,7 @@ to_name_end(char_u *arg, int namespace)
 
 /*
  * Like to_name_end() but also skip over a list or dict constant.
+ * This intentionally does not handle line continuation.
  */
     char_u *
 to_name_const_end(char_u *arg)
@@ -5632,7 +5644,7 @@ compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx)
     static char_u *
 compile_import(char_u *arg, cctx_T *cctx)
 {
-    return handle_import(arg, &cctx->ctx_imports, 0, cctx);
+    return handle_import(arg, &cctx->ctx_imports, 0, NULL, cctx);
 }
 
 /*
index aa6b92cddc4f5541f3f587c48e518ce6a2c8865b..14586f9b8a06501c1c8897f82014ca102d0c318a 100644 (file)
@@ -142,17 +142,21 @@ free_imports(int sid)
     void
 ex_import(exarg_T *eap)
 {
-    char_u *cmd_end;
+    char_u     *cmd_end;
+    evalarg_T  evalarg;
 
     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
     {
        emsg(_("E1094: import can only be used in a script"));
        return;
     }
+    fill_evalarg_from_eap(&evalarg, eap, eap->skip);
 
-    cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
+    cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid,
+                                                              &evalarg, NULL);
     if (cmd_end != NULL)
        eap->nextcmd = check_nextcmd(cmd_end);
+    clear_evalarg(&evalarg, eap);
 }
 
 /*
@@ -164,27 +168,16 @@ ex_import(exarg_T *eap)
     int
 find_exported(
        int         sid,
-       char_u      **argp,
-       int         *name_len,
+       char_u      *name,
        ufunc_T     **ufunc,
        type_T      **type)
 {
-    char_u     *name = *argp;
-    char_u     *arg = *argp;
-    int                cc;
     int                idx = -1;
     svar_T     *sv;
     scriptitem_T *script = SCRIPT_ITEM(sid);
 
-    // isolate one name
-    while (eval_isnamec(*arg))
-       ++arg;
-    *name_len = (int)(arg - name);
-
     // find name in "script"
     // TODO: also find script-local user function
-    cc = *arg;
-    *arg = NUL;
     idx = get_script_item_idx(sid, name, FALSE);
     if (idx >= 0)
     {
@@ -192,7 +185,6 @@ find_exported(
        if (!sv->sv_export)
        {
            semsg(_("E1049: Item not exported in script: %s"), name);
-           *arg = cc;
            return -1;
        }
        *type = sv->sv_type;
@@ -210,10 +202,7 @@ find_exported(
        {
            funcname = alloc(STRLEN(name) + 10);
            if (funcname == NULL)
-           {
-               *arg = cc;
                return -1;
-           }
        }
        funcname[0] = K_SPECIAL;
        funcname[1] = KS_EXTRA;
@@ -226,13 +215,9 @@ find_exported(
        if (*ufunc == NULL)
        {
            semsg(_("E1048: Item not found in script: %s"), name);
-           *arg = cc;
            return -1;
        }
     }
-    *arg = cc;
-    arg = skipwhite(arg);
-    *argp = arg;
 
     return idx;
 }
@@ -243,62 +228,121 @@ find_exported(
  * Returns a pointer to after the command or NULL in case of failure
  */
     char_u *
-handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
+handle_import(
+       char_u      *arg_start,
+       garray_T    *gap,
+       int         import_sid,
+       evalarg_T   *evalarg,
+       void        *cctx)
 {
     char_u     *arg = arg_start;
-    char_u     *cmd_end;
-    char_u     *as_ptr = NULL;
-    char_u     *from_ptr;
-    int                as_len = 0;
+    char_u     *cmd_end = NULL;
+    char_u     *as_name = NULL;
     int                ret = FAIL;
     typval_T   tv;
     int                sid = -1;
     int                res;
+    garray_T   names;
+    static char e_import_syntax[] = N_("E1047: syntax error in import");
 
+    ga_init2(&names, sizeof(char_u *), 10);
     if (*arg == '{')
     {
-       // skip over {item} list
-       while (*arg != NUL && *arg != '}')
-           ++arg;
-       if (*arg == '}')
-           arg = skipwhite(arg + 1);
+       // "import {item, item} from ..."
+       arg = skipwhite_and_linebreak(arg + 1, evalarg);
+       for (;;)
+       {
+           char_u  *p = arg;
+           int     had_comma = FALSE;
+
+           while (eval_isnamec(*arg))
+               ++arg;
+           if (p == arg)
+               break;
+           if (ga_grow(&names, 1) == FAIL)
+               goto erret;
+           ((char_u **)names.ga_data)[names.ga_len] =
+                                                     vim_strnsave(p, arg - p);
+           ++names.ga_len;
+           if (*arg == ',')
+           {
+               had_comma = TRUE;
+               ++arg;
+           }
+           arg = skipwhite_and_linebreak(arg, evalarg);
+           if (*arg == '}')
+           {
+               arg = skipwhite_and_linebreak(arg + 1, evalarg);
+               break;
+           }
+           if (!had_comma)
+           {
+               emsg(_("E1046: Missing comma in import"));
+               goto erret;
+           }
+       }
+       if (names.ga_len == 0)
+       {
+           emsg(_(e_import_syntax));
+           goto erret;
+       }
     }
     else
     {
-       if (*arg == '*')
-           arg = skipwhite(arg + 1);
+       // "import Name from ..."
+       // "import * as Name from ..."
+       // "import item [as Name] from ..."
+       arg = skipwhite_and_linebreak(arg, evalarg);
+       if (arg[0] == '*' && IS_WHITE_OR_NUL(arg[1]))
+           arg = skipwhite_and_linebreak(arg + 1, evalarg);
        else if (eval_isnamec1(*arg))
        {
+           char_u  *p = arg;
+
            while (eval_isnamec(*arg))
                ++arg;
-           arg = skipwhite(arg);
+           if (ga_grow(&names, 1) == FAIL)
+               goto erret;
+           ((char_u **)names.ga_data)[names.ga_len] =
+                                                     vim_strnsave(p, arg - p);
+           ++names.ga_len;
+           arg = skipwhite_and_linebreak(arg, evalarg);
+       }
+       else
+       {
+           emsg(_(e_import_syntax));
+           goto erret;
        }
-       if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
+
+       if (STRNCMP("as", arg, 2) == 0 && IS_WHITE_OR_NUL(arg[2]))
        {
-           // skip over "as Name "
+           char_u *p;
+
+           // skip over "as Name "; no line break allowed after "as"
            arg = skipwhite(arg + 2);
-           as_ptr = arg;
+           p = arg;
            if (eval_isnamec1(*arg))
                while (eval_isnamec(*arg))
                    ++arg;
-           as_len = (int)(arg - as_ptr);
-           arg = skipwhite(arg);
-           if (check_defined(as_ptr, as_len, cctx) == FAIL)
-               return NULL;
+           if (check_defined(p, (int)(arg - p), cctx) == FAIL)
+               goto erret;
+           as_name = vim_strnsave(p, arg - p);
+           arg = skipwhite_and_linebreak(arg, evalarg);
        }
        else if (*arg_start == '*')
        {
            emsg(_("E1045: Missing \"as\" after *"));
-           return NULL;
+           goto erret;
        }
     }
-    if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
+
+    if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4]))
     {
        emsg(_("E1070: Missing \"from\""));
-       return NULL;
+       goto erret;
     }
-    from_ptr = arg;
-    arg = skipwhite(arg + 4);
+
+    arg = skipwhite_and_linebreak_keep_string(arg + 4, evalarg);
     tv.v_type = VAR_UNKNOWN;
     // TODO: should we accept any expression?
     if (*arg == '\'')
@@ -308,11 +352,13 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
     if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
     {
        emsg(_("E1071: Invalid string after \"from\""));
-       return NULL;
+       goto erret;
     }
     cmd_end = arg;
 
-    // find script tv.vval.v_string
+    /*
+     * find script file
+     */
     if (*tv.vval.v_string == '.')
     {
        size_t          len;
@@ -326,7 +372,7 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
        if (from_name == NULL)
        {
            clear_tv(&tv);
-           return NULL;
+           goto erret;
        }
        vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
        add_pathsep(from_name);
@@ -351,7 +397,7 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
        if (from_name == NULL)
        {
            clear_tv(&tv);
-           return 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);
@@ -362,7 +408,7 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
     {
        semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
        clear_tv(&tv);
-       return NULL;
+       goto erret;
     }
     clear_tv(&tv);
 
@@ -372,41 +418,44 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
                                        : &SCRIPT_ITEM(import_sid)->sn_imports);
 
        if (imported == NULL)
-           return NULL;
-       imported->imp_name = vim_strnsave(as_ptr, as_len);
+           goto erret;
+       imported->imp_name = as_name;
+       as_name = NULL;
        imported->imp_sid = sid;
        imported->imp_all = TRUE;
     }
     else
     {
+       int i;
+
        arg = arg_start;
        if (*arg == '{')
            arg = skipwhite(arg + 1);
-       for (;;)
+       for (i = 0; i < names.ga_len; ++i)
        {
-           char_u      *name = arg;
-           int         name_len;
+           char_u      *name = ((char_u **)names.ga_data)[i];
            int         idx;
            imported_T  *imported;
            ufunc_T     *ufunc = NULL;
            type_T      *type;
 
-           idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
+           idx = find_exported(sid, name, &ufunc, &type);
 
            if (idx < 0 && ufunc == NULL)
-               return NULL;
+               goto erret;
 
-           if (check_defined(name, name_len, cctx) == FAIL)
-               return NULL;
+           if (check_defined(name, STRLEN(name), cctx) == FAIL)
+               goto erret;
 
            imported = new_imported(gap != NULL ? gap
                                       : &SCRIPT_ITEM(import_sid)->sn_imports);
            if (imported == NULL)
-               return NULL;
+               goto erret;
 
            // TODO: check for "as" following
-           // imported->imp_name = vim_strnsave(as_ptr, as_len);
-           imported->imp_name = vim_strnsave(name, name_len);
+           // imported->imp_name = vim_strsave(as_name);
+           imported->imp_name = name;
+           ((char_u **)names.ga_data)[i] = NULL;
            imported->imp_sid = sid;
            if (idx >= 0)
            {
@@ -415,30 +464,11 @@ handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
            }
            else
                imported->imp_funcname = ufunc->uf_name;
-
-           arg = skipwhite(arg);
-           if (*arg_start != '{')
-               break;
-           if (*arg == '}')
-           {
-               arg = skipwhite(arg + 1);
-               break;
-           }
-
-           if (*arg != ',')
-           {
-               emsg(_("E1046: Missing comma in import"));
-               return NULL;
-           }
-           arg = skipwhite(arg + 1);
-       }
-       if (arg != from_ptr)
-       {
-           // cannot happen, just in case the above has a flaw
-           emsg(_("E1047: syntax error in import"));
-           return NULL;
        }
     }
+erret:
+    ga_clear_strings(&names);
+    vim_free(as_name);
     return cmd_end;
 }