Patch 8.2.1124
Problem: Vim9: no line break allowed in :import command.
Solution: Skip over line breaks.
Files: src/vim9script.c, src/proto/
vim9script.pro, src/vim9compile.c,
src/testdir/test_vim9_script.vim
*** ../vim-8.2.1123/src/vim9script.c 2020-07-01 18:29:23.685143414 +0200
--- src/vim9script.c 2020-07-04 13:12:10.543506786 +0200
***************
*** 142,158 ****
void
ex_import(exarg_T *eap)
{
! char_u *cmd_end;
if (!getline_equal(eap->getline, eap->cookie, getsourceline))
{
emsg(_("E1094: import can only be used in a script"));
return;
}
! cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
if (cmd_end != NULL)
eap->nextcmd = check_nextcmd(cmd_end);
}
/*
--- 142,162 ----
void
ex_import(exarg_T *eap)
{
! 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,
! &evalarg, NULL);
if (cmd_end != NULL)
eap->nextcmd = check_nextcmd(cmd_end);
+ clear_evalarg(&evalarg, eap);
}
/*
***************
*** 164,190 ****
int
find_exported(
int sid,
! char_u **argp,
! int *name_len,
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)
{
--- 168,183 ----
int
find_exported(
int sid,
! char_u *name,
ufunc_T **ufunc,
type_T **type)
{
int idx = -1;
svar_T *sv;
scriptitem_T *script = SCRIPT_ITEM(sid);
// find name in "script"
// TODO: also find script-local user function
idx = get_script_item_idx(sid, name, FALSE);
if (idx >= 0)
{
***************
*** 192,198 ****
if (!sv->sv_export)
{
semsg(_("E1049: Item not exported in script: %s"), name);
- *arg = cc;
return -1;
}
*type = sv->sv_type;
--- 185,190 ----
***************
*** 210,219 ****
{
funcname = alloc(STRLEN(name) + 10);
if (funcname == NULL)
- {
- *arg = cc;
return -1;
- }
}
funcname[0] = K_SPECIAL;
funcname[1] = KS_EXTRA;
--- 202,208 ----
***************
*** 226,238 ****
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;
}
--- 215,223 ----
***************
*** 243,304 ****
* 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)
{
char_u *arg = arg_start;
! char_u *cmd_end;
! char_u *as_ptr = NULL;
! char_u *from_ptr;
! int as_len = 0;
int ret = FAIL;
typval_T tv;
int sid = -1;
int res;
if (*arg == '{')
{
! // skip over {item} list
! while (*arg != NUL && *arg != '}')
! ++arg;
! if (*arg == '}')
! arg = skipwhite(arg + 1);
}
else
{
! if (*arg == '*')
! arg = skipwhite(arg + 1);
else if (eval_isnamec1(*arg))
{
while (eval_isnamec(*arg))
++arg;
! arg = skipwhite(arg);
}
! if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
{
! // skip over "as Name "
arg = skipwhite(arg + 2);
! as_ptr = 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;
}
else if (*arg_start == '*')
{
emsg(_("E1045: Missing \"as\" after *"));
! return NULL;
}
}
! if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
{
emsg(_("E1070: Missing \"from\""));
! return NULL;
}
! from_ptr = arg;
! arg = skipwhite(arg + 4);
tv.v_type = VAR_UNKNOWN;
// TODO: should we accept any expression?
if (*arg == '\'')
--- 228,348 ----
* 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,
! evalarg_T *evalarg,
! void *cctx)
{
char_u *arg = arg_start;
! 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 == '{')
{
! // "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
{
! // "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;
! 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 && IS_WHITE_OR_NUL(arg[2]))
{
! char_u *p;
!
! // skip over "as Name "; no line break allowed after "as"
arg = skipwhite(arg + 2);
! p = arg;
if (eval_isnamec1(*arg))
while (eval_isnamec(*arg))
++arg;
! 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 *"));
! goto erret;
}
}
!
! if (STRNCMP("from", arg, 4) != 0 || !IS_WHITE_OR_NUL(arg[4]))
{
emsg(_("E1070: Missing \"from\""));
! goto erret;
}
!
! arg = skipwhite_and_linebreak_keep_string(arg + 4, evalarg);
tv.v_type = VAR_UNKNOWN;
// TODO: should we accept any expression?
if (*arg == '\'')
***************
*** 308,318 ****
if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
{
emsg(_("E1071: Invalid string after \"from\""));
! return NULL;
}
cmd_end = arg;
! // find script tv.vval.v_string
if (*tv.vval.v_string == '.')
{
size_t len;
--- 352,364 ----
if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
{
emsg(_("E1071: Invalid string after \"from\""));
! goto erret;
}
cmd_end = arg;
! /*
! * find script file
! */
if (*tv.vval.v_string == '.')
{
size_t len;
***************
*** 326,332 ****
if (from_name == NULL)
{
clear_tv(&tv);
! return NULL;
}
vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
add_pathsep(from_name);
--- 372,378 ----
if (from_name == NULL)
{
clear_tv(&tv);
! goto erret;
}
vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
add_pathsep(from_name);
***************
*** 351,357 ****
if (from_name == NULL)
{
clear_tv(&tv);
! return NULL;
}
vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
--- 397,403 ----
if (from_name == NULL)
{
clear_tv(&tv);
! 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,368 ****
{
semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
clear_tv(&tv);
! return NULL;
}
clear_tv(&tv);
--- 408,414 ----
{
semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
clear_tv(&tv);
! goto erret;
}
clear_tv(&tv);
***************
*** 372,412 ****
: &SCRIPT_ITEM(import_sid)->sn_imports);
if (imported == NULL)
! return NULL;
! imported->imp_name = vim_strnsave(as_ptr, as_len);
imported->imp_sid = sid;
imported->imp_all = TRUE;
}
else
{
arg = arg_start;
if (*arg == '{')
arg = skipwhite(arg + 1);
! for (;;)
{
! char_u *name = arg;
! int name_len;
int idx;
imported_T *imported;
ufunc_T *ufunc = NULL;
type_T *type;
! idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
if (idx < 0 && ufunc == NULL)
! return NULL;
! if (check_defined(name, name_len, cctx) == FAIL)
! return NULL;
imported = new_imported(gap != NULL ? gap
: &SCRIPT_ITEM(import_sid)->sn_imports);
if (imported == NULL)
! return NULL;
// TODO: check for "as" following
! // imported->imp_name = vim_strnsave(as_ptr, as_len);
! imported->imp_name = vim_strnsave(name, name_len);
imported->imp_sid = sid;
if (idx >= 0)
{
--- 418,461 ----
: &SCRIPT_ITEM(import_sid)->sn_imports);
if (imported == NULL)
! 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 (i = 0; i < names.ga_len; ++i)
{
! 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, name, &ufunc, &type);
if (idx < 0 && ufunc == NULL)
! goto erret;
! 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)
! goto erret;
// TODO: check for "as" following
! // 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,444 ****
}
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;
}
}
return cmd_end;
}
--- 464,474 ----
}
else
imported->imp_funcname = ufunc->uf_name;
}
}
+ erret:
+ ga_clear_strings(&names);
+ vim_free(as_name);
return cmd_end;
}
*** ../vim-8.2.1123/src/proto/
vim9script.pro 2020-06-19 18:34:10.989945091 +0200
--- src/proto/
vim9script.pro 2020-07-03 22:57:15.354069062 +0200
***************
*** 4,11 ****
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);
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 : */
--- 4,11 ----
void ex_export(exarg_T *eap);
void free_imports(int sid);
void ex_import(exarg_T *eap);
! 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 : */
*** ../vim-8.2.1123/src/vim9compile.c 2020-07-01 18:29:23.681143435 +0200
--- src/vim9compile.c 2020-07-03 22:55:56.290395603 +0200
***************
*** 2613,2619 ****
if (import->imp_all)
{
char_u *p = skipwhite(*end);
! int name_len;
ufunc_T *ufunc;
type_T *type;
--- 2613,2620 ----
if (import->imp_all)
{
char_u *p = skipwhite(*end);
! char_u *exp_name;
! int cc;
ufunc_T *ufunc;
type_T *type;
***************
*** 2630,2636 ****
return FAIL;
}
! idx = find_exported(import->imp_sid, &p, &name_len, &ufunc, &type);
// TODO: what if it is a function?
if (idx < 0)
return FAIL;
--- 2631,2647 ----
return FAIL;
}
! // 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,2986 ****
--- 2992,2998 ----
/*
* 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,5638 ****
static char_u *
compile_import(char_u *arg, cctx_T *cctx)
{
! return handle_import(arg, &cctx->ctx_imports, 0, cctx);
}
/*
--- 5644,5650 ----
static char_u *
compile_import(char_u *arg, cctx_T *cctx)
{
! return handle_import(arg, &cctx->ctx_imports, 0, NULL, cctx);
}
/*
*** ../vim-8.2.1123/src/testdir/test_vim9_script.vim 2020-07-01 20:07:11.557328210 +0200
--- src/testdir/test_vim9_script.vim 2020-07-04 13:11:46.319570593 +0200
***************
*** 686,691 ****
--- 686,720 ----
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,756 ****
--- 780,800 ----
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'
*** ../vim-8.2.1123/src/version.c 2020-07-03 21:17:31.343914450 +0200
--- src/version.c 2020-07-04 13:13:32.219291562 +0200
***************
*** 756,757 ****
--- 756,759 ----
{ /* Add new patch number below this line */
+ /**/
+ 1124,
/**/
--
"Beware of bugs in the above code; I have only proved
it correct, not tried it." -- Donald Knuth
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language --
http://www.Zimbu.org ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///