blob: ce5cfec922ceaef11f3a9d69842f7582706ef534 [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 {
122 imported_T *imp = ((imported_T *)si->sn_imports.ga_data + idx);
123
124 vim_free(imp->imp_name);
125 }
126 ga_clear(&si->sn_imports);
127}
128
129/*
130 * ":import Item from 'filename'"
131 * ":import Item as Alias from 'filename'"
132 * ":import {Item} from 'filename'".
133 * ":import {Item as Alias} from 'filename'"
134 * ":import {Item, Item} from 'filename'"
135 * ":import {Item, Item as Alias} from 'filename'"
136 *
137 * ":import * as Name from 'filename'"
138 */
139 void
140ex_import(exarg_T *eap)
141{
142 if (current_sctx.sc_version != SCRIPT_VERSION_VIM9)
143 emsg(_(e_needs_vim9));
144 else
145 {
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100146 char_u *cmd_end = handle_import(eap->arg, NULL,
147 current_sctx.sc_sid, NULL);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100148
149 if (cmd_end != NULL)
150 eap->nextcmd = check_nextcmd(cmd_end);
151 }
152}
153
154/*
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100155 * Find an exported item in "sid" matching the name at "*argp".
156 * When it is a variable return the index.
157 * When it is a user function return "*ufunc".
158 * When not found returns -1 and "*ufunc" is NULL.
159 */
160 int
161find_exported(
162 int sid,
163 char_u **argp,
164 int *name_len,
165 ufunc_T **ufunc,
166 type_T **type)
167{
168 char_u *name = *argp;
169 char_u *arg = *argp;
170 int cc;
171 int idx = -1;
172 svar_T *sv;
173 scriptitem_T *script = SCRIPT_ITEM(sid);
174
175 // isolate one name
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100176 while (eval_isnamec(*arg))
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100177 ++arg;
178 *name_len = (int)(arg - name);
179
180 // find name in "script"
181 // TODO: also find script-local user function
182 cc = *arg;
183 *arg = NUL;
184 idx = get_script_item_idx(sid, name, FALSE);
185 if (idx >= 0)
186 {
187 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
188 if (!sv->sv_export)
189 {
190 semsg(_("E1049: Item not exported in script: %s"), name);
191 *arg = cc;
192 return -1;
193 }
194 *type = sv->sv_type;
195 *ufunc = NULL;
196 }
197 else
198 {
199 char_u buffer[200];
200 char_u *funcname;
201
202 // it could be a user function.
203 if (STRLEN(name) < sizeof(buffer) - 10)
204 funcname = buffer;
205 else
206 {
207 funcname = alloc(STRLEN(name) + 10);
208 if (funcname == NULL)
209 {
210 *arg = cc;
211 return -1;
212 }
213 }
214 funcname[0] = K_SPECIAL;
215 funcname[1] = KS_EXTRA;
216 funcname[2] = (int)KE_SNR;
217 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
218 *ufunc = find_func(funcname, NULL);
219 if (funcname != buffer)
220 vim_free(funcname);
221
222 if (*ufunc == NULL)
223 {
224 semsg(_("E1048: Item not found in script: %s"), name);
225 *arg = cc;
226 return -1;
227 }
228 }
229 *arg = cc;
230 arg = skipwhite(arg);
231 *argp = arg;
232
233 return idx;
234}
235
236/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100237 * Handle an ":import" command and add the resulting imported_T to "gap", when
238 * not NULL, or script "import_sid" sn_imports.
239 * Returns a pointer to after the command or NULL in case of failure
240 */
241 char_u *
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100242handle_import(char_u *arg_start, garray_T *gap, int import_sid, void *cctx)
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100243{
244 char_u *arg = arg_start;
245 char_u *cmd_end;
246 char_u *as_ptr = NULL;
247 char_u *from_ptr;
248 int as_len = 0;
249 int ret = FAIL;
250 typval_T tv;
251 int sid = -1;
252 int res;
253
254 if (*arg == '{')
255 {
256 // skip over {item} list
257 while (*arg != NUL && *arg != '}')
258 ++arg;
259 if (*arg == '}')
260 arg = skipwhite(arg + 1);
261 }
262 else
263 {
264 if (*arg == '*')
265 arg = skipwhite(arg + 1);
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100266 else if (eval_isnamec1(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100267 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100268 while (eval_isnamec(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100269 ++arg;
270 arg = skipwhite(arg);
271 }
272 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
273 {
274 // skip over "as Name "
275 arg = skipwhite(arg + 2);
276 as_ptr = arg;
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100277 if (eval_isnamec1(*arg))
278 while (eval_isnamec(*arg))
279 ++arg;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100280 as_len = (int)(arg - as_ptr);
281 arg = skipwhite(arg);
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100282 if (check_defined(as_ptr, as_len, cctx) == FAIL)
283 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100284 }
285 else if (*arg_start == '*')
286 {
287 emsg(_("E1045: Missing \"as\" after *"));
288 return NULL;
289 }
290 }
291 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
292 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100293 emsg(_("E1070: Missing \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100294 return NULL;
295 }
296 from_ptr = arg;
297 arg = skipwhite(arg + 4);
298 tv.v_type = VAR_UNKNOWN;
299 // TODO: should we accept any expression?
300 if (*arg == '\'')
301 ret = get_lit_string_tv(&arg, &tv, TRUE);
302 else if (*arg == '"')
303 ret = get_string_tv(&arg, &tv, TRUE);
304 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
305 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100306 emsg(_("E1071: Invalid string after \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100307 return NULL;
308 }
309 cmd_end = arg;
310
311 // find script tv.vval.v_string
312 if (*tv.vval.v_string == '.')
313 {
314 size_t len;
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100315 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100316 char_u *tail = gettail(si->sn_name);
317 char_u *from_name;
318
319 // Relative to current script: "./name.vim", "../../name.vim".
320 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
321 from_name = alloc((int)len);
322 if (from_name == NULL)
323 {
324 clear_tv(&tv);
325 return NULL;
326 }
327 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
328 add_pathsep(from_name);
329 STRCAT(from_name, tv.vval.v_string);
330 simplify_filename(from_name);
331
332 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
333 vim_free(from_name);
334 }
335 else if (mch_isFullName(tv.vval.v_string))
336 {
337 // Absolute path: "/tmp/name.vim"
338 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
339 }
340 else
341 {
342 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
343 char_u *from_name;
344
345 // Find file in "import" subdirs in 'runtimepath'.
346 from_name = alloc((int)len);
347 if (from_name == NULL)
348 {
349 clear_tv(&tv);
350 return NULL;
351 }
352 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
353 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
354 vim_free(from_name);
355 }
356
357 if (res == FAIL || sid <= 0)
358 {
359 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
360 clear_tv(&tv);
361 return NULL;
362 }
363 clear_tv(&tv);
364
365 if (*arg_start == '*')
366 {
367 imported_T *imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100368 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100369
370 if (imported == NULL)
371 return NULL;
372 imported->imp_name = vim_strnsave(as_ptr, as_len);
373 imported->imp_sid = sid;
374 imported->imp_all = TRUE;
375 }
376 else
377 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100378 arg = arg_start;
379 if (*arg == '{')
380 arg = skipwhite(arg + 1);
381 for (;;)
382 {
383 char_u *name = arg;
384 int name_len;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100385 int idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100386 imported_T *imported;
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100387 ufunc_T *ufunc = NULL;
388 type_T *type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100389
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100390 idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100391
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100392 if (idx < 0 && ufunc == NULL)
393 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100394
Bram Moolenaar5269bd22020-03-09 19:25:27 +0100395 if (check_defined(name, name_len, cctx) == FAIL)
396 return NULL;
397
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100398 imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100399 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100400 if (imported == NULL)
401 return NULL;
402
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100403 // TODO: check for "as" following
404 // imported->imp_name = vim_strnsave(as_ptr, as_len);
405 imported->imp_name = vim_strnsave(name, name_len);
406 imported->imp_sid = sid;
407 if (idx >= 0)
408 {
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100409 imported->imp_type = type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100410 imported->imp_var_vals_idx = idx;
411 }
412 else
413 imported->imp_funcname = ufunc->uf_name;
414
415 arg = skipwhite(arg);
416 if (*arg_start != '{')
417 break;
418 if (*arg == '}')
419 {
420 arg = skipwhite(arg + 1);
421 break;
422 }
423
424 if (*arg != ',')
425 {
426 emsg(_("E1046: Missing comma in import"));
427 return NULL;
428 }
429 arg = skipwhite(arg + 1);
430 }
431 if (arg != from_ptr)
432 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100433 // cannot happen, just in case the above has a flaw
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100434 emsg(_("E1047: syntax error in import"));
435 return NULL;
436 }
437 }
438 return cmd_end;
439}
440
441#endif // FEAT_EVAL