Mercurial > vim
diff 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 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/vim9script.c @@ -0,0 +1,405 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * vim9script.c: :vim9script, :import, :export and friends + */ + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +#include "vim9.h" + +static char e_needs_vim9[] = N_("E1042: import/export can only be used in vim9script"); + + int +in_vim9script(void) +{ + // TODO: go up the stack? + return current_sctx.sc_version == SCRIPT_VERSION_VIM9; +} + +/* + * ":vim9script". + */ + void +ex_vim9script(exarg_T *eap) +{ + scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid); + + if (!getline_equal(eap->getline, eap->cookie, getsourceline)) + { + emsg(_("E1038: vim9script can only be used in a script")); + return; + } + if (si->sn_had_command) + { + emsg(_("E1039: vim9script must be the first command in a script")); + return; + } + current_sctx.sc_version = SCRIPT_VERSION_VIM9; + si->sn_version = SCRIPT_VERSION_VIM9; + si->sn_had_command = TRUE; + + if (STRCMP(p_cpo, CPO_VIM) != 0) + { + si->sn_save_cpo = p_cpo; + p_cpo = vim_strsave((char_u *)CPO_VIM); + } +} + +/* + * ":export let Name: type" + * ":export const Name: type" + * ":export def Name(..." + * ":export class Name ..." + * + * ":export {Name, ...}" + */ + void +ex_export(exarg_T *eap UNUSED) +{ + if (current_sctx.sc_version != SCRIPT_VERSION_VIM9) + { + emsg(_(e_needs_vim9)); + return; + } + + eap->cmd = eap->arg; + (void)find_ex_command(eap, NULL, lookup_scriptvar, NULL); + switch (eap->cmdidx) + { + case CMD_let: + case CMD_const: + case CMD_def: + // case CMD_class: + is_export = TRUE; + do_cmdline(eap->cmd, eap->getline, eap->cookie, + DOCMD_VERBOSE + DOCMD_NOWAIT); + + // The command will reset "is_export" when exporting an item. + if (is_export) + { + emsg(_("E1044: export with invalid argument")); + is_export = FALSE; + } + break; + default: + emsg(_("E1043: Invalid command after :export")); + break; + } +} + +/* + * Add a new imported item entry to the current script. + */ + static imported_T * +new_imported(garray_T *gap) +{ + if (ga_grow(gap, 1) == OK) + return ((imported_T *)gap->ga_data + gap->ga_len++); + return NULL; +} + +/* + * Free all imported items in script "sid". + */ + void +free_imports(int sid) +{ + scriptitem_T *si = &SCRIPT_ITEM(sid); + int idx; + + for (idx = 0; idx < si->sn_imports.ga_len; ++idx) + { + imported_T *imp = ((imported_T *)si->sn_imports.ga_data + idx); + + vim_free(imp->imp_name); + } + ga_clear(&si->sn_imports); +} + +/* + * ":import Item from 'filename'" + * ":import Item as Alias from 'filename'" + * ":import {Item} from 'filename'". + * ":import {Item as Alias} from 'filename'" + * ":import {Item, Item} from 'filename'" + * ":import {Item, Item as Alias} from 'filename'" + * + * ":import * as Name from 'filename'" + */ + void +ex_import(exarg_T *eap) +{ + if (current_sctx.sc_version != SCRIPT_VERSION_VIM9) + emsg(_(e_needs_vim9)); + else + { + char_u *cmd_end = handle_import(eap->arg, NULL, current_sctx.sc_sid); + + if (cmd_end != NULL) + eap->nextcmd = check_nextcmd(cmd_end); + } +} + +/* + * Handle an ":import" command and add the resulting imported_T to "gap", when + * not NULL, or script "import_sid" sn_imports. + * Returns a pointer to after the command or NULL in case of failure + */ + char_u * +handle_import(char_u *arg_start, garray_T *gap, int import_sid) +{ + char_u *arg = arg_start; + char_u *cmd_end; + char_u *as_ptr = NULL; + char_u *from_ptr; + int as_len = 0; + int ret = FAIL; + typval_T tv; + int sid = -1; + int res; + + if (*arg == '{') + { + // skip over {item} list + while (*arg != NUL && *arg != '}') + ++arg; + if (*arg == '}') + arg = skipwhite(arg + 1); + } + else + { + if (*arg == '*') + arg = skipwhite(arg + 1); + else + { + while (eval_isnamec1(*arg)) + ++arg; + arg = skipwhite(arg); + } + if (STRNCMP("as", arg, 2) == 0 && VIM_ISWHITE(arg[2])) + { + // skip over "as Name " + arg = skipwhite(arg + 2); + as_ptr = arg; + while (eval_isnamec1(*arg)) + ++arg; + as_len = (int)(arg - as_ptr); + arg = skipwhite(arg); + } + else if (*arg_start == '*') + { + emsg(_("E1045: Missing \"as\" after *")); + return NULL; + } + } + if (STRNCMP("from", arg, 4) != 0 || !VIM_ISWHITE(arg[4])) + { + emsg(_("E1045: Missing \"from\"")); + return NULL; + } + from_ptr = arg; + arg = skipwhite(arg + 4); + tv.v_type = VAR_UNKNOWN; + // TODO: should we accept any expression? + if (*arg == '\'') + ret = get_lit_string_tv(&arg, &tv, TRUE); + else if (*arg == '"') + ret = get_string_tv(&arg, &tv, TRUE); + if (ret == FAIL || tv.vval.v_string == NULL || *tv.vval.v_string == NUL) + { + emsg(_("E1045: Invalid string after \"from\"")); + return NULL; + } + cmd_end = arg; + + // find script tv.vval.v_string + if (*tv.vval.v_string == '.') + { + size_t len; + scriptitem_T *si = &SCRIPT_ITEM(current_sctx.sc_sid); + char_u *tail = gettail(si->sn_name); + char_u *from_name; + + // Relative to current script: "./name.vim", "../../name.vim". + len = STRLEN(si->sn_name) - STRLEN(tail) + STRLEN(tv.vval.v_string) + 2; + from_name = alloc((int)len); + if (from_name == NULL) + { + clear_tv(&tv); + return NULL; + } + vim_strncpy(from_name, si->sn_name, tail - si->sn_name); + add_pathsep(from_name); + STRCAT(from_name, tv.vval.v_string); + simplify_filename(from_name); + + res = do_source(from_name, FALSE, DOSO_NONE, &sid); + vim_free(from_name); + } + else if (mch_isFullName(tv.vval.v_string)) + { + // Absolute path: "/tmp/name.vim" + res = do_source(tv.vval.v_string, FALSE, DOSO_NONE, &sid); + } + else + { + size_t len = 7 + STRLEN(tv.vval.v_string) + 1; + char_u *from_name; + + // Find file in "import" subdirs in 'runtimepath'. + from_name = alloc((int)len); + if (from_name == NULL) + { + clear_tv(&tv); + return NULL; + } + vim_snprintf((char *)from_name, len, "import/%s", tv.vval.v_string); + res = source_in_path(p_rtp, from_name, DIP_NOAFTER, &sid); + vim_free(from_name); + } + + if (res == FAIL || sid <= 0) + { + semsg(_("E1053: Could not import \"%s\""), tv.vval.v_string); + clear_tv(&tv); + return NULL; + } + clear_tv(&tv); + + if (*arg_start == '*') + { + imported_T *imported = new_imported(gap != NULL ? gap + : &SCRIPT_ITEM(import_sid).sn_imports); + + if (imported == NULL) + return NULL; + imported->imp_name = vim_strnsave(as_ptr, as_len); + imported->imp_sid = sid; + imported->imp_all = TRUE; + } + else + { + scriptitem_T *script = &SCRIPT_ITEM(sid); + + arg = arg_start; + if (*arg == '{') + arg = skipwhite(arg + 1); + for (;;) + { + char_u *name = arg; + int name_len; + int cc; + int idx; + svar_T *sv; + imported_T *imported; + ufunc_T *ufunc; + + // isolate one name + while (eval_isnamec1(*arg)) + ++arg; + name_len = (int)(arg - name); + + // find name in "script" + // TODO: also find script-local user function + cc = *arg; + *arg = NUL; + idx = get_script_item_idx(sid, name, FALSE); + if (idx >= 0) + { + sv = ((svar_T *)script->sn_var_vals.ga_data) + idx; + if (!sv->sv_export) + { + semsg(_("E1049: Item not exported in script: %s"), name); + *arg = cc; + return NULL; + } + } + else + { + char_u buffer[200]; + char_u *funcname; + + // it could be a user function. + if (STRLEN(name) < sizeof(buffer) - 10) + funcname = buffer; + else + { + funcname = alloc(STRLEN(name) + 10); + if (funcname == NULL) + { + *arg = cc; + return NULL; + } + } + funcname[0] = K_SPECIAL; + funcname[1] = KS_EXTRA; + funcname[2] = (int)KE_SNR; + sprintf((char *)funcname + 3, "%ld_%s", (long)sid, name); + ufunc = find_func(funcname, NULL); + if (funcname != buffer) + vim_free(funcname); + + if (ufunc == NULL) + { + semsg(_("E1048: Item not found in script: %s"), name); + *arg = cc; + return NULL; + } + } + + imported = new_imported(gap != NULL ? gap + : &SCRIPT_ITEM(import_sid).sn_imports); + if (imported == NULL) + return NULL; + + *arg = cc; + arg = skipwhite(arg); + + // TODO: check for "as" following + // imported->imp_name = vim_strnsave(as_ptr, as_len); + imported->imp_name = vim_strnsave(name, name_len); + imported->imp_sid = sid; + if (idx >= 0) + { + imported->imp_type = sv->sv_type; + imported->imp_var_vals_idx = idx; + } + else + imported->imp_funcname = ufunc->uf_name; + + arg = skipwhite(arg); + if (*arg_start != '{') + break; + if (*arg == '}') + { + arg = skipwhite(arg + 1); + break; + } + + if (*arg != ',') + { + emsg(_("E1046: Missing comma in import")); + return NULL; + } + arg = skipwhite(arg + 1); + } + if (arg != from_ptr) + { + emsg(_("E1047: syntax error in import")); + return NULL; + } + } + return cmd_end; +} + +#endif // FEAT_EVAL