diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 262 |
1 files changed, 212 insertions, 50 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index fd83bc846b..04899f2c99 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -422,6 +422,7 @@ static struct vimvar { VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), }; #undef VV @@ -433,11 +434,14 @@ static struct vimvar { #define vv_str vv_di.di_tv.vval.v_string #define vv_list vv_di.di_tv.vval.v_list #define vv_dict vv_di.di_tv.vval.v_dict +#define vv_partial vv_di.di_tv.vval.v_partial #define vv_tv vv_di.di_tv /// Variable used for v: static ScopeDictDictItem vimvars_var; +static partial_T *vvlua_partial; + /// v: hashtab #define vimvarht vimvardict.dv_hashtab @@ -639,6 +643,13 @@ void eval_init(void) set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); + vimvars[VV_LUA].vv_type = VAR_PARTIAL; + vvlua_partial = xcalloc(1, sizeof(partial_T)); + vimvars[VV_LUA].vv_partial = vvlua_partial; + // this value shouldn't be printed, but if it is, do not crash + vvlua_partial->pt_name = xmallocz(0); + vvlua_partial->pt_refcount++; + set_reg_var(0); // default for v:register is not 0 but '"' } @@ -1313,12 +1324,25 @@ int call_vim_function( { int doesrange; int ret; + int len = (int)STRLEN(func); + partial_T *pt = NULL; + + if (len >= 6 && !memcmp(func, "v:lua.", 6)) { + func += 6; + len = check_luafunc_name((const char *)func, false); + if (len == 0) { + ret = FAIL; + goto fail; + } + pt = vvlua_partial; + } rettv->v_type = VAR_UNKNOWN; // tv_clear() uses this. - ret = call_func(func, (int)STRLEN(func), rettv, argc, argv, NULL, + ret = call_func(func, len, rettv, argc, argv, NULL, curwin->w_cursor.lnum, curwin->w_cursor.lnum, - &doesrange, true, NULL, NULL); + &doesrange, true, pt, NULL); +fail: if (ret == FAIL) { tv_clear(rettv); } @@ -2462,6 +2486,13 @@ static char_u *get_lval(char_u *const name, typval_T *const rettv, } } + if (lp->ll_di != NULL && tv_is_luafunc(&lp->ll_di->di_tv) + && len == -1 && rettv == NULL) { + tv_clear(&var1); + EMSG2(e_illvar, "v:['lua']"); + return NULL; + } + if (lp->ll_di == NULL) { // Can't add "v:" or "a:" variable. if (lp->ll_dict == &vimvardict @@ -2841,7 +2872,7 @@ void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx) int c; char_u *p; - if (cmdidx == CMD_let) { + if (cmdidx == CMD_let || cmdidx == CMD_const) { xp->xp_context = EXPAND_USER_VARS; if (vim_strpbrk(arg, (char_u *)"\"'+-*/%.=!?~|&$([<>,#") == NULL) { /* ":let var1 var2 ...": find last space. */ @@ -4699,7 +4730,7 @@ eval_index( if (evaluate) { n1 = 0; - if (!empty1 && rettv->v_type != VAR_DICT) { + if (!empty1 && rettv->v_type != VAR_DICT && !tv_is_luafunc(rettv)) { n1 = tv_get_number(&var1); tv_clear(&var1); } @@ -4823,7 +4854,7 @@ eval_index( if (len == -1) { tv_clear(&var1); } - if (item == NULL) { + if (item == NULL || tv_is_luafunc(&item->di_tv)) { return FAIL; } @@ -6334,7 +6365,7 @@ static char_u *deref_func_name(const char *name, int *lenp, */ static int get_func_tv( - char_u *name, // name of the function + const char_u *name, // name of the function int len, // length of "name" typval_T *rettv, char_u **arg, // argument, pointing to the '(' @@ -6590,7 +6621,15 @@ call_func( rettv->vval.v_number = 0; error = ERROR_UNKNOWN; - if (!builtin_function((const char *)rfname, -1)) { + if (partial == vvlua_partial) { + if (len > 0) { + error = ERROR_NONE; + executor_call_lua((const char *)funcname, len, + argvars, argcount, rettv); + } else { + error = ERROR_UNKNOWN; + } + } else if (!builtin_function((const char *)rfname, -1)) { // User defined function. if (partial != NULL && partial->pt_func != NULL) { fp = partial->pt_func; @@ -6707,14 +6746,14 @@ call_func( /// /// @param ermsg must be passed without translation (use N_() instead of _()). /// @param name function name -static void emsg_funcname(char *ermsg, char_u *name) +static void emsg_funcname(char *ermsg, const char_u *name) { char_u *p; if (*name == K_SPECIAL) { p = concat_str((char_u *)"<SNR>", name + 3); } else { - p = name; + p = (char_u *)name; } EMSG2(_(ermsg), p); @@ -8677,18 +8716,25 @@ static void f_environ(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_dict_alloc_ret(rettv); - for (int i = 0; ; i++) { - // TODO(justinmk): use os_copyfullenv from #7202 ? - char *envname = os_getenvname_at_index((size_t)i); - if (envname == NULL) { - break; - } - const char *value = os_getenv(envname); + size_t env_size = os_get_fullenv_size(); + char **env = xmalloc(sizeof(*env) * (env_size + 1)); + env[env_size] = NULL; + + os_copy_fullenv(env, env_size); + + for (size_t i = 0; i < env_size; i++) { + const char * str = env[i]; + const char * const end = strchr(str + (str[0] == '=' ? 1 : 0), + '='); + assert(end != NULL); + ptrdiff_t len = end - str; + assert(len > 0); + const char * value = str + len + 1; tv_dict_add_str(rettv->vval.v_dict, - (char *)envname, STRLEN((char *)envname), - value == NULL ? "" : value); - xfree(envname); + str, len, + value); } + os_free_fullenv(env); } /* @@ -8711,7 +8757,7 @@ static void f_getenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (p == NULL) { rettv->v_type = VAR_SPECIAL; - rettv->vval.v_number = kSpecialVarNull; + rettv->vval.v_special = kSpecialVarNull; return; } rettv->vval.v_string = p; @@ -9876,6 +9922,7 @@ static dict_T *get_buffer_info(buf_T *buf) buf->b_ffname != NULL ? (const char *)buf->b_ffname : ""); tv_dict_add_nr(dict, S_LEN("lnum"), buf == curbuf ? curwin->w_cursor.lnum : buflist_findlnum(buf)); + tv_dict_add_nr(dict, S_LEN("linecount"), buf->b_ml.ml_line_count); tv_dict_add_nr(dict, S_LEN("loaded"), buf->b_ml.ml_mfp != NULL); tv_dict_add_nr(dict, S_LEN("listed"), buf->b_p_bl); tv_dict_add_nr(dict, S_LEN("changed"), bufIsChanged(buf)); @@ -11464,6 +11511,9 @@ static void f_glob2regpat(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) { static const char *const has_list[] = { +#if defined(BSD) && !defined(__APPLE__) + "bsd", +#endif #ifdef UNIX "unix", #endif @@ -12535,6 +12585,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool executable = true; char **argv = tv_to_argv(&argvars[0], NULL, &executable); + char **env = NULL; if (!argv) { rettv->vval.v_number = executable ? 0 : -1; return; // Did error message in tv_to_argv. @@ -12552,6 +12603,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) bool detach = false; bool rpc = false; bool pty = false; + bool clear_env = false; CallbackReader on_stdout = CALLBACK_READER_INIT, on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; @@ -12562,6 +12614,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) detach = tv_dict_get_number(job_opts, "detach") != 0; rpc = tv_dict_get_number(job_opts, "rpc") != 0; pty = tv_dict_get_number(job_opts, "pty") != 0; + clear_env = tv_dict_get_number(job_opts, "clear_env") != 0; if (pty && rpc) { EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set"); shell_free_argv(argv); @@ -12578,6 +12631,45 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } } + dictitem_T *job_env = tv_dict_find(job_opts, S_LEN("env")); + if (job_env) { + if (job_env->di_tv.v_type != VAR_DICT) { + EMSG2(_(e_invarg2), "env"); + shell_free_argv(argv); + return; + } + + size_t custom_env_size = (size_t)tv_dict_len(job_env->di_tv.vval.v_dict); + size_t i = 0; + size_t env_size = 0; + + if (clear_env) { + // + 1 for last null entry + env = xmalloc((custom_env_size + 1) * sizeof(*env)); + env_size = 0; + } else { + env_size = os_get_fullenv_size(); + + env = xmalloc((custom_env_size + env_size + 1) * sizeof(*env)); + + os_copy_fullenv(env, env_size); + i = env_size; + } + assert(env); // env must be allocated at this point + + TV_DICT_ITER(job_env->di_tv.vval.v_dict, var, { + const char *str = tv_get_string(&var->di_tv); + assert(str); + size_t len = STRLEN(var->di_key) + strlen(str) + strlen("=") + 1; + env[i] = xmalloc(len); + snprintf(env[i], len, "%s=%s", (char *)var->di_key, str); + i++; + }); + + // must be null terminated + env[env_size + custom_env_size] = NULL; + } + if (!common_job_callbacks(job_opts, &on_stdout, &on_stderr, &on_exit)) { shell_free_argv(argv); @@ -12595,8 +12687,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) } Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty, - rpc, detach, cwd, width, height, term_name, - &rettv->vval.v_number); + rpc, detach, cwd, width, height, + term_name, env, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); } @@ -12870,7 +12962,7 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) const char *libname = (char *) argvars[0].vval.v_string; const char *funcname = (char *) argvars[1].vval.v_string; - int in_type = argvars[2].v_type; + VarType in_type = argvars[2].v_type; // input variables char *str_in = (in_type == VAR_STRING) @@ -12879,8 +12971,8 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) // output variables char **str_out = (out_type == VAR_STRING) - ? (char **) &rettv->vval.v_string : NULL; - int64_t int_out = 0; + ? (char **)&rettv->vval.v_string : NULL; + int int_out = 0; bool success = os_libcall(libname, funcname, str_in, int_in, @@ -12892,7 +12984,7 @@ static void libcall_common(typval_T *argvars, typval_T *rettv, int out_type) } if (out_type == VAR_NUMBER) { - rettv->vval.v_number = (int) int_out; + rettv->vval.v_number = (varnumber_T)int_out; } } @@ -13881,6 +13973,13 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "pum_getpos()" function +static void f_pum_getpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + pum_set_event_info(rettv->vval.v_dict); +} + /* * "pumvisible()" function */ @@ -14891,7 +14990,7 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT, CALLBACK_READER_INIT, CALLBACK_NONE, - false, true, false, NULL, 0, 0, NULL, + false, true, false, NULL, 0, 0, NULL, NULL, &rettv->vval.v_number); if (chan) { channel_create_event(chan, NULL); @@ -15669,7 +15768,7 @@ static void f_setenv(typval_T *argvars, typval_T *rettv, FunPtr fptr) const char *name = tv_get_string_buf(&argvars[0], namebuf); if (argvars[1].v_type == VAR_SPECIAL - && argvars[1].vval.v_number == kSpecialVarNull) { + && argvars[1].vval.v_special == kSpecialVarNull) { os_unsetenv(name); } else { os_setenv(name, tv_get_string_buf(&argvars[1], valbuf), 1); @@ -18278,7 +18377,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, true, false, false, cwd, term_width, curwin->w_height_inner, - xstrdup("xterm-256color"), + xstrdup("xterm-256color"), NULL, &rettv->vval.v_number); if (rettv->vval.v_number <= 0) { return; @@ -20168,6 +20267,26 @@ static void check_vars(const char *name, size_t len) } } +/// check if special v:lua value for calling lua functions +static bool tv_is_luafunc(typval_T *tv) +{ + return tv->v_type == VAR_PARTIAL && tv->vval.v_partial == vvlua_partial; +} + +/// check the function name after "v:lua." +static int check_luafunc_name(const char *str, bool paren) +{ + const char *p = str; + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.') { + p++; + } + if (*p != (paren ? '(' : NUL)) { + return 0; + } else { + return (int)(p-str); + } +} + /// Handle expr[expr], expr[expr:expr] subscript and .name lookup. /// Also handle function call with Funcref variable: func(expr) /// Can all be combined: dict.func(expr)[idx]['func'](expr) @@ -20181,9 +20300,30 @@ handle_subscript( { int ret = OK; dict_T *selfdict = NULL; - char_u *s; + const char_u *s; int len; typval_T functv; + int slen = 0; + bool lua = false; + + if (tv_is_luafunc(rettv)) { + if (**arg != '.') { + tv_clear(rettv); + ret = FAIL; + } else { + (*arg)++; + + lua = true; + s = (char_u *)(*arg); + slen = check_luafunc_name(*arg, true); + if (slen == 0) { + tv_clear(rettv); + ret = FAIL; + } + (*arg) += slen; + } + } + while (ret == OK && (**arg == '[' @@ -20200,14 +20340,16 @@ handle_subscript( // Invoke the function. Recursive! if (functv.v_type == VAR_PARTIAL) { pt = functv.vval.v_partial; - s = partial_name(pt); + if (!lua) { + s = partial_name(pt); + } } else { s = functv.vval.v_string; } } else { s = (char_u *)""; } - ret = get_func_tv(s, (int)STRLEN(s), rettv, (char_u **)arg, + ret = get_func_tv(s, lua ? slen : (int)STRLEN(s), rettv, (char_u **)arg, curwin->w_cursor.lnum, curwin->w_cursor.lnum, &len, evaluate, pt, selfdict); @@ -20442,7 +20584,7 @@ static hashtab_T *get_funccal_local_ht(void) return &get_funccal()->l_vars.dv_hashtab; } -/// Find the dict and hashtable used for a variable +/// Finds the dict (g:, l:, s:, …) and hashtable used for a variable. /// /// @param[in] name Variable name, possibly with scope prefix. /// @param[in] name_len Variable name length. @@ -21742,22 +21884,31 @@ void ex_function(exarg_T *eap) } // Check for ":let v =<< [trim] EOF" + // and ":let [a, b] =<< [trim] EOF" arg = skipwhite(skiptowhite(p)); - arg = skipwhite(skiptowhite(arg)); - if (arg[0] == '=' && arg[1] == '<' && arg[2] =='<' - && ((p[0] == 'l' && p[1] == 'e' - && (!ASCII_ISALNUM(p[2]) - || (p[2] == 't' && !ASCII_ISALNUM(p[3])))))) { - p = skipwhite(arg + 3); - if (STRNCMP(p, "trim", 4) == 0) { - // Ignore leading white space. - p = skipwhite(p + 4); - heredoc_trimmed = vim_strnsave(theline, - (int)(skipwhite(theline) - theline)); + if (*arg == '[') { + arg = vim_strchr(arg, ']'); + } + if (arg != NULL) { + arg = skipwhite(skiptowhite(arg)); + if (arg[0] == '=' + && arg[1] == '<' + && arg[2] =='<' + && (p[0] == 'l' + && p[1] == 'e' + && (!ASCII_ISALNUM(p[2]) + || (p[2] == 't' && !ASCII_ISALNUM(p[3]))))) { + p = skipwhite(arg + 3); + if (STRNCMP(p, "trim", 4) == 0) { + // Ignore leading white space. + p = skipwhite(p + 4); + heredoc_trimmed = + vim_strnsave(theline, (int)(skipwhite(theline) - theline)); + } + skip_until = vim_strnsave(p, (int)(skiptowhite(p) - p)); + do_concat = false; + is_heredoc = true; } - skip_until = vim_strnsave(p, (int)(skiptowhite(p) - p)); - do_concat = false; - is_heredoc = true; } } @@ -22030,8 +22181,19 @@ trans_function_name( *pp = (char_u *)end; } else if (lv.ll_tv->v_type == VAR_PARTIAL && lv.ll_tv->vval.v_partial != NULL) { - name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); - *pp = (char_u *)end; + if (lv.ll_tv->vval.v_partial == vvlua_partial && *end == '.') { + len = check_luafunc_name((const char *)end+1, true); + if (len == 0) { + EMSG2(e_invexpr2, "v:lua"); + goto theend; + } + name = xmallocz(len); + memcpy(name, end+1, len); + *pp = (char_u *)end+1+len; + } else { + name = vim_strsave(partial_name(lv.ll_tv->vval.v_partial)); + *pp = (char_u *)end; + } if (partial != NULL) { *partial = lv.ll_tv->vval.v_partial; } @@ -23491,7 +23653,7 @@ void ex_return(exarg_T *eap) int do_return(exarg_T *eap, int reanimate, int is_cmd, void *rettv) { int idx; - struct condstack *cstack = eap->cstack; + cstack_T *const cstack = eap->cstack; if (reanimate) /* Undo the return. */ |