blob: ca1470269341447dfb77640e02ae91c695d0907d [file] [log] [blame]
Bram Moolenaar8a7d6542020-01-26 15:56:19 +01001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * vim9script.c: :vim9script, :import, :export and friends
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
18#include "vim9.h"
19
Bram Moolenaar9721fb42020-06-11 23:10:46 +020020static char e_needs_vim9[] = N_("E1042: export can only be used in vim9script");
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010021
22 int
23in_vim9script(void)
24{
25 // TODO: go up the stack?
26 return current_sctx.sc_version == SCRIPT_VERSION_VIM9;
27}
28
29/*
30 * ":vim9script".
31 */
32 void
33ex_vim9script(exarg_T *eap)
34{
Bram Moolenaar09689a02020-05-09 22:50:08 +020035 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010036
37 if (!getline_equal(eap->getline, eap->cookie, getsourceline))
38 {
39 emsg(_("E1038: vim9script can only be used in a script"));
40 return;
41 }
42 if (si->sn_had_command)
43 {
44 emsg(_("E1039: vim9script must be the first command in a script"));
45 return;
46 }
47 current_sctx.sc_version = SCRIPT_VERSION_VIM9;
48 si->sn_version = SCRIPT_VERSION_VIM9;
49 si->sn_had_command = TRUE;
50
51 if (STRCMP(p_cpo, CPO_VIM) != 0)
52 {
53 si->sn_save_cpo = p_cpo;
54 p_cpo = vim_strsave((char_u *)CPO_VIM);
55 }
56}
57
58/*
59 * ":export let Name: type"
60 * ":export const Name: type"
61 * ":export def Name(..."
62 * ":export class Name ..."
63 *
64 * ":export {Name, ...}"
65 */
66 void
Bram Moolenaar09689a02020-05-09 22:50:08 +020067ex_export(exarg_T *eap)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +010068{
69 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
70 {
71 emsg(_(e_needs_vim9));
72 return;
73 }
74
75 eap->cmd = eap->arg;
76 (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL);
77 switch (eap->cmdidx)
78 {
79 case CMD_let:
80 case CMD_const:
81 case CMD_def:
82 // case CMD_class:
83 is_export = TRUE;
84 do_cmdline(eap->cmd, eap->getline, eap->cookie,
85 DOCMD_VERBOSE + DOCMD_NOWAIT);
86
87 // The command will reset "is_export" when exporting an item.
88 if (is_export)
89 {
90 emsg(_("E1044: export with invalid argument"));
91 is_export = FALSE;
92 }
93 break;
94 default:
95 emsg(_("E1043: Invalid command after :export"));
96 break;
97 }
98}
99
100/*
101 * Add a new imported item entry to the current script.
102 */
103 static imported_T *
104new_imported(garray_T *gap)
105{
106 if (ga_grow(gap, 1) == OK)
107 return ((imported_T *)gap->ga_data + gap->ga_len++);
108 return NULL;
109}
110
111/*
112 * Free all imported items in script "sid".
113 */
114 void
115free_imports(int sid)
116{
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100117 scriptitem_T *si = SCRIPT_ITEM(sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100118 int idx;
119
120 for (idx = 0; idx < si->sn_imports.ga_len; ++idx)
121 {
Bram Moolenaar20431c92020-03-20 18:39:46 +0100122 imported_T *imp = ((imported_T *)si->sn_imports.ga_data) + idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100123
124 vim_free(imp->imp_name);
125 }
126 ga_clear(&si->sn_imports);
Bram Moolenaar20431c92020-03-20 18:39:46 +0100127 ga_clear(&si->sn_var_vals);
128 ga_clear(&si->sn_type_list);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100129}
130
131/*
132 * ":import Item from 'filename'"
133 * ":import Item as Alias from 'filename'"
134 * ":import {Item} from 'filename'".
135 * ":import {Item as Alias} from 'filename'"
136 * ":import {Item, Item} from 'filename'"
137 * ":import {Item, Item as Alias} from 'filename'"
138 *
139 * ":import * as Name from 'filename'"
140 */
141 void
142ex_import(exarg_T *eap)
143{
Bram Moolenaar9721fb42020-06-11 23:10:46 +0200144 char_u *cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid, NULL);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100145
Bram Moolenaar9721fb42020-06-11 23:10:46 +0200146 if (cmd_end != NULL)
147 eap->nextcmd = check_nextcmd(cmd_end);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100148}
149
150/*
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100151 * Find an exported item in "sid" matching the name at "*argp".
152 * When it is a variable return the index.
153 * When it is a user function return "*ufunc".
154 * When not found returns -1 and "*ufunc" is NULL.
155 */
156 int
157find_exported(
158 int sid,
159 char_u **argp,
160 int *name_len,
161 ufunc_T **ufunc,
162 type_T **type)
163{
164 char_u *name = *argp;
165 char_u *arg = *argp;
166 int cc;
167 int idx = -1;
168 svar_T *sv;
169 scriptitem_T *script = SCRIPT_ITEM(sid);
170
171 // isolate one name
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100172 while (eval_isnamec(*arg))
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100173 ++arg;
174 *name_len = (int)(arg - name);
175
176 // find name in "script"
177 // TODO: also find script-local user function
178 cc = *arg;
179 *arg = NUL;
180 idx = get_script_item_idx(sid, name, FALSE);
181 if (idx >= 0)
182 {
183 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
184 if (!sv->sv_export)
185 {
186 semsg(_("E1049: Item not exported in script: %s"), name);
187 *arg = cc;
188 return -1;
189 }
190 *type = sv->sv_type;
191 *ufunc = NULL;
192 }
193 else
194 {
195 char_u buffer[200];
196 char_u *funcname;
197
198 // it could be a user function.
199 if (STRLEN(name) < sizeof(buffer) - 10)
200 funcname = buffer;
201 else
202 {
203 funcname = alloc(STRLEN(name) + 10);
204 if (funcname == NULL)
205 {
206 *arg = cc;
207 return -1;
208 }
209 }
210 funcname[0] = K_SPECIAL;
211 funcname[1] = KS_EXTRA;
212 funcname[2] = (int)KE_SNR;
213 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
Bram Moolenaar4c17ad92020-04-27 22:47:51 +0200214 *ufunc = find_func(funcname, FALSE, NULL);
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100215 if (funcname != buffer)
216 vim_free(funcname);
217
218 if (*ufunc == NULL)
219 {
220 semsg(_("E1048: Item not found in script: %s"), name);
221 *arg = cc;
222 return -1;
223 }
224 }
225 *arg = cc;
226 arg = skipwhite(arg);
227 *argp = arg;
228
229 return idx;
230}
231
232/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100233 * Handle an ":import" command and add the resulting imported_T to "gap", when
234 * not NULL, or script "import_sid" sn_imports.
235 * Returns a pointer to after the command or NULL in case of failure
236 */
237 char_u *
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100238handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100239{
240 char_u *arg = arg_start;
241 char_u *cmd_end;
242 char_u *as_ptr = NULL;
243 char_u *from_ptr;
244 int as_len = 0;
245 int ret = FAIL;
246 typval_T tv;
247 int sid = -1;
248 int res;
249
250 if (*arg == '{')
251 {
252 // skip over {item} list
253 while (*arg != NUL && *arg != '}')
254 ++arg;
255 if (*arg == '}')
256 arg = skipwhite(arg + 1);
257 }
258 else
259 {
260 if (*arg == '*')
261 arg = skipwhite(arg + 1);
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100262 else if (eval_isnamec1(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100263 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100264 while (eval_isnamec(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100265 ++arg;
266 arg = skipwhite(arg);
267 }
268 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
269 {
270 // skip over "as Name "
271 arg = skipwhite(arg + 2);
272 as_ptr = arg;
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100273 if (eval_isnamec1(*arg))
274 while (eval_isnamec(*arg))
275 ++arg;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100276 as_len = (int)(arg - as_ptr);
277 arg = skipwhite(arg);
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100278 if (check_defined(as_ptr, as_len, cctx) == FAIL)
279 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100280 }
281 else if (*arg_start == '*')
282 {
283 emsg(_("E1045: Missing \"as\" after *"));
284 return NULL;
285 }
286 }
287 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
288 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100289 emsg(_("E1070: Missing \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100290 return NULL;
291 }
292 from_ptr = arg;
293 arg = skipwhite(arg + 4);
294 tv.v_type = VAR_UNKNOWN;
295 // TODO: should we accept any expression?
296 if (*arg == '\'')
297 ret = get_lit_string_tv(&arg, &tv, TRUE);
298 else if (*arg == '"')
299 ret = get_string_tv(&arg, &tv, TRUE);
300 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
301 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100302 emsg(_("E1071: Invalid string after \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100303 return NULL;
304 }
305 cmd_end = arg;
306
307 // find script tv.vval.v_string
308 if (*tv.vval.v_string == '.')
309 {
310 size_t len;
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100311 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100312 char_u *tail = gettail(si->sn_name);
313 char_u *from_name;
314
315 // Relative to current script: "./name.vim", "../../name.vim".
316 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
317 from_name = alloc((int)len);
318 if (from_name == NULL)
319 {
320 clear_tv(&tv);
321 return NULL;
322 }
323 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
324 add_pathsep(from_name);
325 STRCAT(from_name, tv.vval.v_string);
326 simplify_filename(from_name);
327
328 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
329 vim_free(from_name);
330 }
331 else if (mch_isFullName(tv.vval.v_string))
332 {
333 // Absolute path: "/tmp/name.vim"
334 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
335 }
336 else
337 {
338 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
339 char_u *from_name;
340
341 // Find file in "import" subdirs in 'runtimepath'.
342 from_name = alloc((int)len);
343 if (from_name == NULL)
344 {
345 clear_tv(&tv);
346 return NULL;
347 }
348 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
349 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
350 vim_free(from_name);
351 }
352
353 if (res == FAIL || sid <= 0)
354 {
355 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
356 clear_tv(&tv);
357 return NULL;
358 }
359 clear_tv(&tv);
360
361 if (*arg_start == '*')
362 {
363 imported_T *imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100364 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100365
366 if (imported == NULL)
367 return NULL;
368 imported->imp_name = vim_strnsave(as_ptr, as_len);
369 imported->imp_sid = sid;
370 imported->imp_all = TRUE;
371 }
372 else
373 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100374 arg = arg_start;
375 if (*arg == '{')
376 arg = skipwhite(arg + 1);
377 for (;;)
378 {
379 char_u *name = arg;
380 int name_len;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100381 int idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100382 imported_T *imported;
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100383 ufunc_T *ufunc = NULL;
384 type_T *type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100385
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100386 idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100387
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100388 if (idx < 0 && ufunc == NULL)
389 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100390
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100391 if (check_defined(name, name_len, cctx) == FAIL)
392 return NULL;
393
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100394 imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100395 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100396 if (imported == NULL)
397 return NULL;
398
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100399 // TODO: check for "as" following
400 // imported->imp_name = vim_strnsave(as_ptr, as_len);
401 imported->imp_name = vim_strnsave(name, name_len);
402 imported->imp_sid = sid;
403 if (idx >= 0)
404 {
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100405 imported->imp_type = type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100406 imported->imp_var_vals_idx = idx;
407 }
408 else
409 imported->imp_funcname = ufunc->uf_name;
410
411 arg = skipwhite(arg);
412 if (*arg_start != '{')
413 break;
414 if (*arg == '}')
415 {
416 arg = skipwhite(arg + 1);
417 break;
418 }
419
420 if (*arg != ',')
421 {
422 emsg(_("E1046: Missing comma in import"));
423 return NULL;
424 }
425 arg = skipwhite(arg + 1);
426 }
427 if (arg != from_ptr)
428 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100429 // cannot happen, just in case the above has a flaw
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100430 emsg(_("E1047: syntax error in import"));
431 return NULL;
432 }
433 }
434 return cmd_end;
435}
436
437#endif // FEAT_EVAL