comparison src/vim9script.c @ 19181:94eda51ba9ba v8.2.0149

patch 8.2.0149: maintaining a Vim9 branch separately is more work Commit: https://github.com/vim/vim/commit/8a7d6542b33e5d2b352262305c3bfdb2d14e1cf8 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jan 26 15:56:19 2020 +0100 patch 8.2.0149: maintaining a Vim9 branch separately is more work Problem: Maintaining a Vim9 branch separately is more work. Solution: Merge the Vim9 script changes.
author Bram Moolenaar <Bram@vim.org>
date Sun, 26 Jan 2020 16:00:05 +0100
parents
children 133ef7ba4e4e
comparison
equal deleted inserted replaced
19180:8edf0aeb71b9 19181:94eda51ba9ba
1 /* 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
20 static char e_needs_vim9[] = N_("E1042: import/export can only be used in vim9script");
21
22 int
23 in_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
33 ex_vim9script(exarg_T *eap)
34 {
35 scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
36
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
67 ex_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 *
104 new_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
115 free_imports(int sid)
116 {
117 scriptitem_T *si = &SCRIPT_ITEM(sid);
118 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
140 ex_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 /*
154 * Handle an ":import" command and add the resulting imported_T to "gap", when
155 * not NULL, or script "import_sid" sn_imports.
156 * Returns a pointer to after the command or NULL in case of failure
157 */
158 char_u *
159 handle_import(char_u *arg_start, garray_T *gap, int import_sid)
160 {
161 char_u *arg = arg_start;
162 char_u *cmd_end;
163 char_u *as_ptr = NULL;
164 char_u *from_ptr;
165 int as_len = 0;
166 int ret = FAIL;
167 typval_T tv;
168 int sid = -1;
169 int res;
170
171 if (*arg == '{')
172 {
173 // skip over {item} list
174 while (*arg != NUL && *arg != '}')
175 ++arg;
176 if (*arg == '}')
177 arg = skipwhite(arg + 1);
178 }
179 else
180 {
181 if (*arg == '*')
182 arg = skipwhite(arg + 1);
183 else
184 {
185 while (eval_isnamec1(*arg))
186 ++arg;
187 arg = skipwhite(arg);
188 }
189 if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2]))
190 {
191 // skip over "as Name "
192 arg = skipwhite(arg + 2);
193 as_ptr = arg;
194 while (eval_isnamec1(*arg))
195 ++arg;
196 as_len = (int)(arg - as_ptr);
197 arg = skipwhite(arg);
198 }
199 else if (*arg_start == '*')
200 {
201 emsg(_("E1045: Missing \"as\" after *"));
202 return NULL;
203 }
204 }
205 if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4]))
206 {
207 emsg(_("E1045: Missing \"from\""));
208 return NULL;
209 }
210 from_ptr = arg;
211 arg = skipwhite(arg + 4);
212 tv.v_type = VAR_UNKNOWN;
213 // TODO: should we accept any expression?
214 if (*arg == '\'')
215 ret = get_lit_string_tv(&arg, &tv, TRUE);
216 else if (*arg == '"')
217 ret = get_string_tv(&arg, &tv, TRUE);
218 if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL)
219 {
220 emsg(_("E1045: Invalid string after \"from\""));
221 return NULL;
222 }
223 cmd_end = arg;
224
225 // find script tv.vval.v_string
226 if (*tv.vval.v_string == '.')
227 {
228 size_t len;
229 scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid);
230 char_u *tail = gettail(si->sn_name);
231 char_u *from_name;
232
233 // Relative to current script: "./name.vim", "../../name.vim".
234 len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2;
235 from_name = alloc((int)len);
236 if (from_name == NULL)
237 {
238 clear_tv(&tv);
239 return NULL;
240 }
241 vim_strncpy(from_name, si->sn_name, tail - si->sn_name);
242 add_pathsep(from_name);
243 STRCAT(from_name, tv.vval.v_string);
244 simplify_filename(from_name);
245
246 res = do_source(from_name, FALSE, DOSO_NONE, &sid);
247 vim_free(from_name);
248 }
249 else if (mch_isFullName(tv.vval.v_string))
250 {
251 // Absolute path: "/tmp/name.vim"
252 res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid);
253 }
254 else
255 {
256 size_t len = 7 + STRLEN(tv.vval.v_string) + 1;
257 char_u *from_name;
258
259 // Find file in "import" subdirs in 'runtimepath'.
260 from_name = alloc((int)len);
261 if (from_name == NULL)
262 {
263 clear_tv(&tv);
264 return NULL;
265 }
266 vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string);
267 res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid);
268 vim_free(from_name);
269 }
270
271 if (res == FAIL || sid <= 0)
272 {
273 semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string);
274 clear_tv(&tv);
275 return NULL;
276 }
277 clear_tv(&tv);
278
279 if (*arg_start == '*')
280 {
281 imported_T *imported = new_imported(gap != NULL ? gap
282 : &SCRIPT_ITEM(import_sid).sn_imports);
283
284 if (imported == NULL)
285 return NULL;
286 imported->imp_name = vim_strnsave(as_ptr, as_len);
287 imported->imp_sid = sid;
288 imported->imp_all = TRUE;
289 }
290 else
291 {
292 scriptitem_T *script = &SCRIPT_ITEM(sid);
293
294 arg = arg_start;
295 if (*arg == '{')
296 arg = skipwhite(arg + 1);
297 for (;;)
298 {
299 char_u *name = arg;
300 int name_len;
301 int cc;
302 int idx;
303 svar_T *sv;
304 imported_T *imported;
305 ufunc_T *ufunc;
306
307 // isolate one name
308 while (eval_isnamec1(*arg))
309 ++arg;
310 name_len = (int)(arg - name);
311
312 // find name in "script"
313 // TODO: also find script-local user function
314 cc = *arg;
315 *arg = NUL;
316 idx = get_script_item_idx(sid, name, FALSE);
317 if (idx >= 0)
318 {
319 sv = ((svar_T *)script->sn_var_vals.ga_data) + idx;
320 if (!sv->sv_export)
321 {
322 semsg(_("E1049: Item not exported in script: %s"), name);
323 *arg = cc;
324 return NULL;
325 }
326 }
327 else
328 {
329 char_u buffer[200];
330 char_u *funcname;
331
332 // it could be a user function.
333 if (STRLEN(name) < sizeof(buffer) - 10)
334 funcname = buffer;
335 else
336 {
337 funcname = alloc(STRLEN(name) + 10);
338 if (funcname == NULL)
339 {
340 *arg = cc;
341 return NULL;
342 }
343 }
344 funcname[0] = K_SPECIAL;
345 funcname[1] = KS_EXTRA;
346 funcname[2] = (int)KE_SNR;
347 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name);
348 ufunc = find_func(funcname, NULL);
349 if (funcname != buffer)
350 vim_free(funcname);
351
352 if (ufunc == NULL)
353 {
354 semsg(_("E1048: Item not found in script: %s"), name);
355 *arg = cc;
356 return NULL;
357 }
358 }
359
360 imported = new_imported(gap != NULL ? gap
361 : &SCRIPT_ITEM(import_sid).sn_imports);
362 if (imported == NULL)
363 return NULL;
364
365 *arg = cc;
366 arg = skipwhite(arg);
367
368 // TODO: check for "as" following
369 // imported->imp_name = vim_strnsave(as_ptr, as_len);
370 imported->imp_name = vim_strnsave(name, name_len);
371 imported->imp_sid = sid;
372 if (idx >= 0)
373 {
374 imported->imp_type = sv->sv_type;
375 imported->imp_var_vals_idx = idx;
376 }
377 else
378 imported->imp_funcname = ufunc->uf_name;
379
380 arg = skipwhite(arg);
381 if (*arg_start != '{')
382 break;
383 if (*arg == '}')
384 {
385 arg = skipwhite(arg + 1);
386 break;
387 }
388
389 if (*arg != ',')
390 {
391 emsg(_("E1046: Missing comma in import"));
392 return NULL;
393 }
394 arg = skipwhite(arg + 1);
395 }
396 if (arg != from_ptr)
397 {
398 emsg(_("E1047: syntax error in import"));
399 return NULL;
400 }
401 }
402 return cmd_end;
403 }
404
405 #endif // FEAT_EVAL