blob: afd6a4af97815a1efb453c325de53d05c0a2c026 [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
20static char e_needs_vim9[] = N_("E1042: import/export can only be used in vim9script");
21
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 Moolenaar21b9e972020-01-26 19:26:46 +010035 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
67ex_export(exarg_T *eap UNUSED)
68{
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{
144 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
145 emsg(_(e_needs_vim9));
146 else
147 {
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100148 char_u *cmd_end = handle_import(eap->arg, NULL,
149 current_sctx.sc_sid, NULL);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100150
151 if (cmd_end != NULL)
152 eap->nextcmd = check_nextcmd(cmd_end);
153 }
154}
155
156/*
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100157 * Find an exported item in "sid" matching the name at "*argp".
158 * When it is a variable return the index.
159 * When it is a user function return "*ufunc".
160 * When not found returns -1 and "*ufunc" is NULL.
161 */
162 int
163find_exported(
164 int sid,
165 char_u **argp,
166 int *name_len,
167 ufunc_T **ufunc,
168 type_T **type)
169{
170 char_u *name = *argp;
171 char_u *arg = *argp;
172 int cc;
173 int idx = -1;
174 svar_T *sv;
175 scriptitem_T *script = SCRIPT_ITEM(sid);
176
177 // isolate one name
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100178 while (eval_isnamec(*arg))
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100179 ++arg;
180 *name_len = (int)(arg - name);
181
182 // find name in "script"
183 // TODO: also find script-local user function
184 cc = *arg;
185 *arg = NUL;
186 idx = get_script_item_idx(sid, name, FALSE);
187 if (idx >= 0)
188 {
189 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
190 if (!sv->sv_export)
191 {
192 semsg(_("E1049: Item not exported in script: %s"), name);
193 *arg = cc;
194 return -1;
195 }
196 *type = sv->sv_type;
197 *ufunc = NULL;
198 }
199 else
200 {
201 char_u buffer[200];
202 char_u *funcname;
203
204 // it could be a user function.
205 if (STRLEN(name) < sizeof(buffer) - 10)
206 funcname = buffer;
207 else
208 {
209 funcname = alloc(STRLEN(name) + 10);
210 if (funcname == NULL)
211 {
212 *arg = cc;
213 return -1;
214 }
215 }
216 funcname[0] = K_SPECIAL;
217 funcname[1] = KS_EXTRA;
218 funcname[2] = (int)KE_SNR;
219 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
Bram Moolenaar4c17ad92020-04-27 22:47:51 +0200220 *ufunc = find_func(funcname, FALSE, NULL);
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100221 if (funcname != buffer)
222 vim_free(funcname);
223
224 if (*ufunc == NULL)
225 {
226 semsg(_("E1048: Item not found in script: %s"), name);
227 *arg = cc;
228 return -1;
229 }
230 }
231 *arg = cc;
232 arg = skipwhite(arg);
233 *argp = arg;
234
235 return idx;
236}
237
238/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100239 * Handle an ":import" command and add the resulting imported_T to "gap", when
240 * not NULL, or script "import_sid" sn_imports.
241 * Returns a pointer to after the command or NULL in case of failure
242 */
243 char_u *
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100244handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100245{
246 char_u *arg = arg_start;
247 char_u *cmd_end;
248 char_u *as_ptr = NULL;
249 char_u *from_ptr;
250 int as_len = 0;
251 int ret = FAIL;
252 typval_T tv;
253 int sid = -1;
254 int res;
255
256 if (*arg == '{')
257 {
258 // skip over {item} list
259 while (*arg != NUL && *arg != '}')
260 ++arg;
261 if (*arg == '}')
262 arg = skipwhite(arg + 1);
263 }
264 else
265 {
266 if (*arg == '*')
267 arg = skipwhite(arg + 1);
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100268 else if (eval_isnamec1(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100269 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100270 while (eval_isnamec(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100271 ++arg;
272 arg = skipwhite(arg);
273 }
274 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
275 {
276 // skip over "as Name "
277 arg = skipwhite(arg + 2);
278 as_ptr = arg;
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100279 if (eval_isnamec1(*arg))
280 while (eval_isnamec(*arg))
281 ++arg;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100282 as_len = (int)(arg - as_ptr);
283 arg = skipwhite(arg);
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100284 if (check_defined(as_ptr, as_len, cctx) == FAIL)
285 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100286 }
287 else if (*arg_start == '*')
288 {
289 emsg(_("E1045: Missing \"as\" after *"));
290 return NULL;
291 }
292 }
293 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
294 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100295 emsg(_("E1070: Missing \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100296 return NULL;
297 }
298 from_ptr = arg;
299 arg = skipwhite(arg + 4);
300 tv.v_type = VAR_UNKNOWN;
301 // TODO: should we accept any expression?
302 if (*arg == '\'')
303 ret = get_lit_string_tv(&arg, &tv, TRUE);
304 else if (*arg == '"')
305 ret = get_string_tv(&arg, &tv, TRUE);
306 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
307 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100308 emsg(_("E1071: Invalid string after \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100309 return NULL;
310 }
311 cmd_end = arg;
312
313 // find script tv.vval.v_string
314 if (*tv.vval.v_string == '.')
315 {
316 size_t len;
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100317 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100318 char_u *tail = gettail(si->sn_name);
319 char_u *from_name;
320
321 // Relative to current script: "./name.vim", "../../name.vim".
322 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
323 from_name = alloc((int)len);
324 if (from_name == NULL)
325 {
326 clear_tv(&tv);
327 return NULL;
328 }
329 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
330 add_pathsep(from_name);
331 STRCAT(from_name, tv.vval.v_string);
332 simplify_filename(from_name);
333
334 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
335 vim_free(from_name);
336 }
337 else if (mch_isFullName(tv.vval.v_string))
338 {
339 // Absolute path: "/tmp/name.vim"
340 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
341 }
342 else
343 {
344 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
345 char_u *from_name;
346
347 // Find file in "import" subdirs in 'runtimepath'.
348 from_name = alloc((int)len);
349 if (from_name == NULL)
350 {
351 clear_tv(&tv);
352 return NULL;
353 }
354 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
355 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
356 vim_free(from_name);
357 }
358
359 if (res == FAIL || sid <= 0)
360 {
361 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
362 clear_tv(&tv);
363 return NULL;
364 }
365 clear_tv(&tv);
366
367 if (*arg_start == '*')
368 {
369 imported_T *imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100370 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100371
372 if (imported == NULL)
373 return NULL;
374 imported->imp_name = vim_strnsave(as_ptr, as_len);
375 imported->imp_sid = sid;
376 imported->imp_all = TRUE;
377 }
378 else
379 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100380 arg = arg_start;
381 if (*arg == '{')
382 arg = skipwhite(arg + 1);
383 for (;;)
384 {
385 char_u *name = arg;
386 int name_len;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100387 int idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100388 imported_T *imported;
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100389 ufunc_T *ufunc = NULL;
390 type_T *type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100391
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100392 idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100393
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100394 if (idx < 0 && ufunc == NULL)
395 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100396
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100397 if (check_defined(name, name_len, cctx) == FAIL)
398 return NULL;
399
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100400 imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100401 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100402 if (imported == NULL)
403 return NULL;
404
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100405 // TODO: check for "as" following
406 // imported->imp_name = vim_strnsave(as_ptr, as_len);
407 imported->imp_name = vim_strnsave(name, name_len);
408 imported->imp_sid = sid;
409 if (idx >= 0)
410 {
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100411 imported->imp_type = type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100412 imported->imp_var_vals_idx = idx;
413 }
414 else
415 imported->imp_funcname = ufunc->uf_name;
416
417 arg = skipwhite(arg);
418 if (*arg_start != '{')
419 break;
420 if (*arg == '}')
421 {
422 arg = skipwhite(arg + 1);
423 break;
424 }
425
426 if (*arg != ',')
427 {
428 emsg(_("E1046: Missing comma in import"));
429 return NULL;
430 }
431 arg = skipwhite(arg + 1);
432 }
433 if (arg != from_ptr)
434 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100435 // cannot happen, just in case the above has a flaw
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100436 emsg(_("E1047: syntax error in import"));
437 return NULL;
438 }
439 }
440 return cmd_end;
441}
442
443#endif // FEAT_EVAL