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 ufunc = NULL; 327 } 328 else 329 { 330 char_u buffer[200]; 331 char_u *funcname; 332 333 // it could be a user function. 334 if (STRLEN(name) < sizeof(buffer) - 10) 335 funcname = buffer; 336 else 337 { 338 funcname = alloc(STRLEN(name) + 10); 339 if (funcname == NULL) 340 { 341 *arg = cc; 342 return NULL; 343 } 344 } 345 funcname[0] = K_SPECIAL; 346 funcname[1] = KS_EXTRA; 347 funcname[2] = (int)KE_SNR; 348 sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); 349 ufunc = find_func(funcname, NULL); 350 if (funcname != buffer) 351 vim_free(funcname); 352 353 if (ufunc == NULL) 354 { 355 semsg(_("E1048: Item not found in script: %s"), name); 356 *arg = cc; 357 return NULL; 358 } 359 sv = NULL; 360 } 361 *arg = cc; 362 arg = skipwhite(arg); 363 364 imported = new_imported(gap != NULL ? gap 365 : &SCRIPT_ITEM(import_sid)->sn_imports); 366 if (imported == NULL) 367 return NULL; 368 369 // TODO: check for "as" following 370 // imported->imp_name = vim_strnsave(as_ptr, as_len); 371 imported->imp_name = vim_strnsave(name, name_len); 372 imported->imp_sid = sid; 373 if (idx >= 0) 374 { 375 imported->imp_type = sv->sv_type; 376 imported->imp_var_vals_idx = idx; 377 } 378 else 379 imported->imp_funcname = ufunc->uf_name; 380 381 arg = skipwhite(arg); 382 if (*arg_start != '{') 383 break; 384 if (*arg == '}') 385 { 386 arg = skipwhite(arg + 1); 387 break; 388 } 389 390 if (*arg != ',') 391 { 392 emsg(_("E1046: Missing comma in import")); 393 return NULL; 394 } 395 arg = skipwhite(arg + 1); 396 } 397 if (arg != from_ptr) 398 { 399 emsg(_("E1047: syntax error in import")); 400 return NULL; 401 } 402 } 403 return cmd_end; 404 } 405 406 #endif // FEAT_EVAL 407