blob: 6cea96bd044e95a21ffa9d05da6fbfd5fa977e75 [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 {
146 char_u *cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid);
147
148 if (cmd_end != NULL)
149 eap->nextcmd = check_nextcmd(cmd_end);
150 }
151}
152
153/*
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100154 * Find an exported item in "sid" matching the name at "*argp".
155 * When it is a variable return the index.
156 * When it is a user function return "*ufunc".
157 * When not found returns -1 and "*ufunc" is NULL.
158 */
159 int
160find_exported(
161 int sid,
162 char_u **argp,
163 int *name_len,
164 ufunc_T **ufunc,
165 type_T **type)
166{
167 char_u *name = *argp;
168 char_u *arg = *argp;
169 int cc;
170 int idx = -1;
171 svar_T *sv;
172 scriptitem_T *script = SCRIPT_ITEM(sid);
173
174 // isolate one name
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100175 while (eval_isnamec(*arg))
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100176 ++arg;
177 *name_len = (int)(arg - name);
178
179 // find name in "script"
180 // TODO: also find script-local user function
181 cc = *arg;
182 *arg = NUL;
183 idx = get_script_item_idx(sid, name, FALSE);
184 if (idx >= 0)
185 {
186 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
187 if (!sv->sv_export)
188 {
189 semsg(_("E1049: Item not exported in script: %s"), name);
190 *arg = cc;
191 return -1;
192 }
193 *type = sv->sv_type;
194 *ufunc = NULL;
195 }
196 else
197 {
198 char_u buffer[200];
199 char_u *funcname;
200
201 // it could be a user function.
202 if (STRLEN(name) < sizeof(buffer) - 10)
203 funcname = buffer;
204 else
205 {
206 funcname = alloc(STRLEN(name) + 10);
207 if (funcname == NULL)
208 {
209 *arg = cc;
210 return -1;
211 }
212 }
213 funcname[0] = K_SPECIAL;
214 funcname[1] = KS_EXTRA;
215 funcname[2] = (int)KE_SNR;
216 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
217 *ufunc = find_func(funcname, NULL);
218 if (funcname != buffer)
219 vim_free(funcname);
220
221 if (*ufunc == NULL)
222 {
223 semsg(_("E1048: Item not found in script: %s"), name);
224 *arg = cc;
225 return -1;
226 }
227 }
228 *arg = cc;
229 arg = skipwhite(arg);
230 *argp = arg;
231
232 return idx;
233}
234
235/*
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100236 * Handle an ":import" command and add the resulting imported_T to "gap", when
237 * not NULL, or script "import_sid" sn_imports.
238 * Returns a pointer to after the command or NULL in case of failure
239 */
240 char_u *
241handle_import(char_u *arg_start, garray_T *gap, int import_sid)
242{
243 char_u *arg = arg_start;
244 char_u *cmd_end;
245 char_u *as_ptr = NULL;
246 char_u *from_ptr;
247 int as_len = 0;
248 int ret = FAIL;
249 typval_T tv;
250 int sid = -1;
251 int res;
252
253 if (*arg == '{')
254 {
255 // skip over {item} list
256 while (*arg != NUL && *arg != '}')
257 ++arg;
258 if (*arg == '}')
259 arg = skipwhite(arg + 1);
260 }
261 else
262 {
263 if (*arg == '*')
264 arg = skipwhite(arg + 1);
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100265 else if (eval_isnamec1(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100266 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100267 while (eval_isnamec(*arg))
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100268 ++arg;
269 arg = skipwhite(arg);
270 }
271 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
272 {
273 // skip over "as Name "
274 arg = skipwhite(arg + 2);
275 as_ptr = arg;
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100276 if (eval_isnamec1(*arg))
277 while (eval_isnamec(*arg))
278 ++arg;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100279 as_len = (int)(arg - as_ptr);
280 arg = skipwhite(arg);
281 }
282 else if (*arg_start == '*')
283 {
284 emsg(_("E1045: Missing \"as\" after *"));
285 return NULL;
286 }
287 }
288 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
289 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100290 emsg(_("E1070: Missing \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100291 return NULL;
292 }
293 from_ptr = arg;
294 arg = skipwhite(arg + 4);
295 tv.v_type = VAR_UNKNOWN;
296 // TODO: should we accept any expression?
297 if (*arg == '\'')
298 ret = get_lit_string_tv(&arg, &tv, TRUE);
299 else if (*arg == '"')
300 ret = get_string_tv(&arg, &tv, TRUE);
301 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
302 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100303 emsg(_("E1071: Invalid string after \"from\""));
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100304 return NULL;
305 }
306 cmd_end = arg;
307
308 // find script tv.vval.v_string
309 if (*tv.vval.v_string == '.')
310 {
311 size_t len;
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100312 scriptitem_T *si = SCRIPT_ITEM(current_sctx.sc_sid);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100313 char_u *tail = gettail(si->sn_name);
314 char_u *from_name;
315
316 // Relative to current script: "./name.vim", "../../name.vim".
317 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
318 from_name = alloc((int)len);
319 if (from_name == NULL)
320 {
321 clear_tv(&tv);
322 return NULL;
323 }
324 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
325 add_pathsep(from_name);
326 STRCAT(from_name, tv.vval.v_string);
327 simplify_filename(from_name);
328
329 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
330 vim_free(from_name);
331 }
332 else if (mch_isFullName(tv.vval.v_string))
333 {
334 // Absolute path: "/tmp/name.vim"
335 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
336 }
337 else
338 {
339 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
340 char_u *from_name;
341
342 // Find file in "import" subdirs in 'runtimepath'.
343 from_name = alloc((int)len);
344 if (from_name == NULL)
345 {
346 clear_tv(&tv);
347 return NULL;
348 }
349 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
350 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
351 vim_free(from_name);
352 }
353
354 if (res == FAIL || sid <= 0)
355 {
356 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
357 clear_tv(&tv);
358 return NULL;
359 }
360 clear_tv(&tv);
361
362 if (*arg_start == '*')
363 {
364 imported_T *imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100365 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100366
367 if (imported == NULL)
368 return NULL;
369 imported->imp_name = vim_strnsave(as_ptr, as_len);
370 imported->imp_sid = sid;
371 imported->imp_all = TRUE;
372 }
373 else
374 {
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100375 arg = arg_start;
376 if (*arg == '{')
377 arg = skipwhite(arg + 1);
378 for (;;)
379 {
380 char_u *name = arg;
381 int name_len;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100382 int idx;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100383 imported_T *imported;
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100384 ufunc_T *ufunc = NULL;
385 type_T *type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100386
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100387 idx = find_exported(sid, &arg, &name_len, &ufunc, &type);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100388
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100389 if (idx < 0 && ufunc == NULL)
390 return NULL;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100391
392 imported = new_imported(gap != NULL ? gap
Bram Moolenaar21b9e972020-01-26 19:26:46 +0100393 : &SCRIPT_ITEM(import_sid)->sn_imports);
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100394 if (imported == NULL)
395 return NULL;
396
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100397 // TODO: check for "as" following
398 // imported->imp_name = vim_strnsave(as_ptr, as_len);
399 imported->imp_name = vim_strnsave(name, name_len);
400 imported->imp_sid = sid;
401 if (idx >= 0)
402 {
Bram Moolenaarf2d5c242020-02-23 21:25:54 +0100403 imported->imp_type = type;
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100404 imported->imp_var_vals_idx = idx;
405 }
406 else
407 imported->imp_funcname = ufunc->uf_name;
408
409 arg = skipwhite(arg);
410 if (*arg_start != '{')
411 break;
412 if (*arg == '}')
413 {
414 arg = skipwhite(arg + 1);
415 break;
416 }
417
418 if (*arg != ',')
419 {
420 emsg(_("E1046: Missing comma in import"));
421 return NULL;
422 }
423 arg = skipwhite(arg + 1);
424 }
425 if (arg != from_ptr)
426 {
Bram Moolenaarfa29c8a2020-02-23 22:35:05 +0100427 // cannot happen, just in case the above has a flaw
Bram Moolenaar8a7d6542020-01-26 15:56:19 +0100428 emsg(_("E1047: syntax error in import"));
429 return NULL;
430 }
431 }
432 return cmd_end;
433}
434
435#endif // FEAT_EVAL