diff options
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r-- | src/nvim/eval.c | 551 |
1 files changed, 418 insertions, 133 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 5ef2a8772e..6479163028 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -241,13 +241,14 @@ typedef enum { ///< the value (prevents error message). } GetLvalFlags; -// function flags +// flags used in uf_flags #define FC_ABORT 0x01 // abort function on error #define FC_RANGE 0x02 // function accepts range #define FC_DICT 0x04 // Dict function, uses "self" #define FC_CLOSURE 0x08 // closure, uses outer scope variables #define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0 #define FC_REMOVED 0x20 // function redefined while uf_refcount > 0 +#define FC_SANDBOX 0x40 // function defined in the sandbox // The names of packages that once were loaded are remembered. static garray_T ga_loaded = { 0, 0, sizeof(char_u *), 4, NULL }; @@ -448,6 +449,7 @@ typedef struct { int timer_id; int repeat_count; int refcount; + int emsg_count; ///< Errors in a repeating timer. long timeout; bool stopped; bool paused; @@ -3933,7 +3935,7 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) int op; varnumber_T n1, n2; bool use_float = false; - float_T f1 = 0, f2; + float_T f1 = 0, f2 = 0; bool error = false; /* @@ -5219,7 +5221,7 @@ bool garbage_collect(bool testing) (void)garbage_collect(testing); } } else if (p_verbose > 0) { - verb_msg((char_u *)_( + verb_msg(_( "Not enough memory to set references, garbage collection aborted!")); } #undef ABORTING @@ -5853,6 +5855,9 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) if (prof_def_func()) { func_do_profile(fp); } + if (sandbox) { + flags |= FC_SANDBOX; + } fp->uf_varargs = true; fp->uf_flags = flags; fp->uf_calls = 0; @@ -6328,8 +6333,12 @@ call_func( } - /* execute the function if no errors detected and executing */ - if (evaluate && error == ERROR_NONE) { + // Execute the function if executing and no errors were detected. + if (!evaluate) { + // Not evaluating, which means the return value is unknown. This + // matters for giving error messages. + rettv->v_type = VAR_UNKNOWN; + } else if (error == ERROR_NONE) { char_u *rfname = fname; /* Ignore "g:" before a function name. */ @@ -6654,12 +6663,24 @@ static void f_append(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = 1; /* Failed */ } -/* - * "argc()" function - */ static void f_argc(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = ARGCOUNT; + if (argvars[0].v_type == VAR_UNKNOWN) { + // use the current window + rettv->vval.v_number = ARGCOUNT; + } else if (argvars[0].v_type == VAR_NUMBER + && tv_get_number(&argvars[0]) == -1) { + // use the global argument list + rettv->vval.v_number = GARGCOUNT; + } else { + // use the argument list of the specified window + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp != NULL) { + rettv->vval.v_number = WARGCOUNT(wp); + } else { + rettv->vval.v_number = -1; + } + } } /* @@ -6680,28 +6701,54 @@ static void f_arglistid(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// Get the argument list for a given window +static void get_arglist_as_rettv(aentry_T *arglist, int argcount, + typval_T *rettv) +{ + tv_list_alloc_ret(rettv, argcount); + if (arglist != NULL) { + for (int idx = 0; idx < argcount; idx++) { + tv_list_append_string(rettv->vval.v_list, + (const char *)alist_name(&arglist[idx]), -1); + } + } +} + /* * "argv(nr)" function */ static void f_argv(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - int idx; + aentry_T *arglist = NULL; + int argcount = -1; if (argvars[0].v_type != VAR_UNKNOWN) { - idx = (int)tv_get_number_chk(&argvars[0], NULL); - if (idx >= 0 && idx < ARGCOUNT) { - rettv->vval.v_string = (char_u *)xstrdup( - (const char *)alist_name(&ARGLIST[idx])); + if (argvars[1].v_type == VAR_UNKNOWN) { + arglist = ARGLIST; + argcount = ARGCOUNT; + } else if (argvars[1].v_type == VAR_NUMBER + && tv_get_number(&argvars[1]) == -1) { + arglist = GARGLIST; + argcount = GARGCOUNT; } else { - rettv->vval.v_string = NULL; + win_T *wp = find_win_by_nr_or_id(&argvars[1]); + if (wp != NULL) { + // Use the argument list of the specified window + arglist = WARGLIST(wp); + argcount = WARGCOUNT(wp); + } } rettv->v_type = VAR_STRING; - } else { - tv_list_alloc_ret(rettv, ARGCOUNT); - for (idx = 0; idx < ARGCOUNT; idx++) { - tv_list_append_string(rettv->vval.v_list, - (const char *)alist_name(&ARGLIST[idx]), -1); + rettv->vval.v_string = NULL; + int idx = tv_get_number_chk(&argvars[0], NULL); + if (arglist != NULL && idx >= 0 && idx < argcount) { + rettv->vval.v_string = (char_u *)xstrdup( + (const char *)alist_name(&arglist[idx])); + } else if (idx == -1) { + get_arglist_as_rettv(arglist, argcount, rettv); } + } else { + get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv); } } @@ -6830,6 +6877,27 @@ static void assert_equal_common(typval_T *argvars, assert_type_T atype) } } +static void f_assert_beeps(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + const char *const cmd = tv_get_string_chk(&argvars[0]); + garray_T ga; + + called_vim_beep = false; + suppress_errthrow = true; + emsg_silent = false; + do_cmdline_cmd(cmd); + if (!called_vim_beep) { + prepare_assert_error(&ga); + ga_concat(&ga, (const char_u *)"command did not beep: "); + ga_concat(&ga, (const char_u *)cmd); + assert_error(&ga); + ga_clear(&ga); + } + + suppress_errthrow = false; + emsg_on_display = false; +} + // "assert_equal(expected, actual[, msg])" function static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -7556,6 +7624,23 @@ static void f_complete_check(typval_T *argvars, typval_T *rettv, FunPtr fptr) RedrawingDisabled = saved; } +// "complete_info()" function +static void f_complete_info(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + + list_T *what_list = NULL; + + if (argvars[0].v_type != VAR_UNKNOWN) { + if (argvars[0].v_type != VAR_LIST) { + EMSG(_(e_listreq)); + return; + } + what_list = argvars[0].vval.v_list; + } + get_complete_info(what_list, rettv->vval.v_dict); +} + /* * "confirm(message, buttons[, default [, type]])" function */ @@ -7983,7 +8068,7 @@ static void f_diff_hlID(typval_T *argvars, typval_T *rettv, FunPtr fptr) hlID = HLF_CHD; // Changed line. } } - rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)hlID; + rettv->vval.v_number = hlID == (hlf_T)0 ? 0 : (int)(hlID + 1); } /* @@ -8194,6 +8279,19 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = path; } +/// Find a window: When using a Window ID in any tab page, when using a number +/// in the current tab page. +win_T * find_win_by_nr_or_id(typval_T *vp) +{ + int nr = (int)tv_get_number_chk(vp, NULL); + + if (nr >= LOWEST_WIN_ID) { + return win_id2wp(vp); + } + + return find_win_by_nr(vp, NULL); +} + /* * "exists()" function */ @@ -9953,6 +10051,37 @@ static void f_getftype(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_string = type; } +// "getjumplist()" function +static void f_getjumplist(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + win_T *const wp = find_tabwin(&argvars[0], &argvars[1]); + if (wp == NULL) { + return; + } + + cleanup_jumplist(wp, true); + + list_T *const l = tv_list_alloc(wp->w_jumplistlen); + tv_list_append_list(rettv->vval.v_list, l); + tv_list_append_number(rettv->vval.v_list, wp->w_jumplistidx); + + for (int i = 0; i < wp->w_jumplistlen; i++) { + if (wp->w_jumplist[i].fmark.mark.lnum == 0) { + continue; + } + dict_T *const d = tv_dict_alloc(); + tv_list_append_dict(l, d); + tv_dict_add_nr(d, S_LEN("lnum"), wp->w_jumplist[i].fmark.mark.lnum); + tv_dict_add_nr(d, S_LEN("col"), wp->w_jumplist[i].fmark.mark.col); + tv_dict_add_nr(d, S_LEN("coladd"), wp->w_jumplist[i].fmark.mark.coladd); + tv_dict_add_nr(d, S_LEN("bufnr"), wp->w_jumplist[i].fmark.fnum); + if (wp->w_jumplist[i].fname != NULL) { + tv_dict_add_str(d, S_LEN("filename"), (char *)wp->w_jumplist[i].fname); + } + } +} + /* * "getline(lnum, [end])" function */ @@ -9988,7 +10117,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, dict_T *d = what_arg->vval.v_dict; if (d != NULL) { - get_errorlist_properties(wp, d, rettv->vval.v_dict); + qf_get_properties(wp, d, rettv->vval.v_dict); } } else { EMSG(_(e_dictreq)); @@ -10000,7 +10129,7 @@ static void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, /// "getloclist()" function static void f_getloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp = find_win_by_nr(&argvars[0], NULL); + win_T *wp = find_win_by_nr_or_id(&argvars[0]); get_qf_loc_list(false, wp, &argvars[1], rettv); } @@ -10019,7 +10148,7 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // match added with matchaddpos() for (i = 0; i < MAXPOSMATCH; i++) { llpos_T *llpos; - char buf[6]; + char buf[30]; // use 30 to avoid compiler warning llpos = &cur->pos.pos[i]; if (llpos->lnum == 0) { @@ -10306,6 +10435,23 @@ static void f_gettabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) getwinvar(argvars, rettv, 1); } +// "gettagstack()" function +static void f_gettagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + win_T *wp = curwin; // default is current window + + tv_dict_alloc_ret(rettv); + + if (argvars[0].v_type != VAR_UNKNOWN) { + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + return; + } + } + + get_tagstack(wp, rettv->vval.v_dict); +} + /// Returns information about a window as a dictionary. static dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { @@ -10371,11 +10517,19 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { tv_list_alloc_ret(rettv, 2); - const win_T *const wp = find_win_by_nr(&argvars[0], NULL); + const win_T *const wp = find_win_by_nr_or_id(&argvars[0]); tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_winrow + 1); tv_list_append_number(rettv->vval.v_list, wp == NULL ? 0 : wp->w_wincol + 1); } +// "getwinpos({timeout})" function +static void f_getwinpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_list_alloc_ret(rettv, 2); + tv_list_append_number(rettv->vval.v_list, -1); + tv_list_append_number(rettv->vval.v_list, -1); +} + /* * "getwinposx()" function */ @@ -10707,6 +10861,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) #ifdef __APPLE__ "mac", "macunix", + "osx", #endif "menu", "mksession", @@ -11180,7 +11335,6 @@ static void f_index(typval_T *argvars, typval_T *rettv, FunPtr fptr) static int inputsecret_flag = 0; - /* * This function is used by f_input() and f_inputdialog() functions. The third * argument to f_input() specifies the type of completion to use at the @@ -11369,25 +11523,21 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, FunPtr fptr) static garray_T ga_userinput = {0, 0, sizeof(tasave_T), 4, NULL}; -/* - * "inputrestore()" function - */ +/// "inputrestore()" function static void f_inputrestore(typval_T *argvars, typval_T *rettv, FunPtr fptr) { if (!GA_EMPTY(&ga_userinput)) { - --ga_userinput.ga_len; + ga_userinput.ga_len--; restore_typeahead((tasave_T *)(ga_userinput.ga_data) - + ga_userinput.ga_len); - /* default return is zero == OK */ + + ga_userinput.ga_len); + // default return is zero == OK } else if (p_verbose > 1) { - verb_msg((char_u *)_("called inputrestore() more often than inputsave()")); - rettv->vval.v_number = 1; /* Failed */ + verb_msg(_("called inputrestore() more often than inputsave()")); + rettv->vval.v_number = 1; // Failed } } -/* - * "inputsave()" function - */ +/// "inputsave()" function static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) { // Add an entry to the stack of typeahead storage. @@ -11395,9 +11545,7 @@ static void f_inputsave(typval_T *argvars, typval_T *rettv, FunPtr fptr) save_typeahead(p); } -/* - * "inputsecret()" function - */ +/// "inputsecret()" function static void f_inputsecret(typval_T *argvars, typval_T *rettv, FunPtr fptr) { cmdline_star++; @@ -11564,10 +11712,11 @@ static void dict_list(typval_T *const tv, typval_T *const rettv, static void f_id(typval_T *argvars, typval_T *rettv, FunPtr fptr) FUNC_ATTR_NONNULL_ALL { - const int len = vim_vsnprintf(NULL, 0, "%p", dummy_ap, argvars); + const int len = vim_vsnprintf_typval(NULL, 0, "%p", dummy_ap, argvars); rettv->v_type = VAR_STRING; rettv->vval.v_string = xmalloc(len + 1); - vim_vsnprintf((char *)rettv->vval.v_string, len + 1, "%p", dummy_ap, argvars); + vim_vsnprintf_typval((char *)rettv->vval.v_string, len + 1, "%p", + dummy_ap, argvars); } /* @@ -12503,6 +12652,31 @@ static void f_match(typval_T *argvars, typval_T *rettv, FunPtr fptr) find_some_match(argvars, rettv, kSomeMatch); } +static int matchadd_dict_arg(typval_T *tv, const char **conceal_char, + win_T **win) +{ + dictitem_T *di; + + if (tv->v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return FAIL; + } + + if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("conceal"))) != NULL) { + *conceal_char = tv_get_string(&di->di_tv); + } + + if ((di = tv_dict_find(tv->vval.v_dict, S_LEN("window"))) != NULL) { + *win = find_win_by_nr_or_id(&di->di_tv); + if (*win == NULL) { + EMSG(_("E957: Invalid window number")); + return FAIL; + } + } + + return OK; +} + /* * "matchadd()" function */ @@ -12516,6 +12690,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) int id = -1; bool error = false; const char *conceal_char = NULL; + win_T *win = curwin; rettv->vval.v_number = -1; @@ -12526,16 +12701,9 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) { - if (argvars[4].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - dictitem_T *di; - if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) - != NULL) { - conceal_char = tv_get_string(&di->di_tv); - } + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { + return; } } } @@ -12547,8 +12715,7 @@ static void f_matchadd(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, grp, pat, prio, id, NULL, - conceal_char); + rettv->vval.v_number = match_add(win, grp, pat, prio, id, NULL, conceal_char); } static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) @@ -12576,21 +12743,15 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) int prio = 10; int id = -1; const char *conceal_char = NULL; + win_T *win = curwin; if (argvars[2].v_type != VAR_UNKNOWN) { prio = tv_get_number_chk(&argvars[2], &error); if (argvars[3].v_type != VAR_UNKNOWN) { id = tv_get_number_chk(&argvars[3], &error); - if (argvars[4].v_type != VAR_UNKNOWN) { - if (argvars[4].v_type != VAR_DICT) { - EMSG(_(e_dictreq)); - return; - } - dictitem_T *di; - if ((di = tv_dict_find(argvars[4].vval.v_dict, S_LEN("conceal"))) - != NULL) { - conceal_char = tv_get_string(&di->di_tv); - } + if (argvars[4].v_type != VAR_UNKNOWN + && matchadd_dict_arg(&argvars[4], &conceal_char, &win) == FAIL) { + return; } } } @@ -12604,8 +12765,7 @@ static void f_matchaddpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) return; } - rettv->vval.v_number = match_add(curwin, group, NULL, prio, id, l, - conceal_char); + rettv->vval.v_number = match_add(win, group, NULL, prio, id, l, conceal_char); } /* @@ -12752,33 +12912,36 @@ static void f_mkdir(typval_T *argvars, typval_T *rettv, FunPtr fptr) char buf[NUMBUFLEN]; const char *const dir = tv_get_string_buf(&argvars[0], buf); if (*dir == NUL) { - rettv->vval.v_number = FAIL; - } else { - if (*path_tail((char_u *)dir) == NUL) { - // Remove trailing slashes. - *path_tail_with_sep((char_u *)dir) = NUL; - } + return; + } - if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) { - prot = tv_get_number_chk(&argvars[2], NULL); - } - if (prot != -1 && strcmp(tv_get_string(&argvars[1]), "p") == 0) { - char *failed_dir; - int ret = os_mkdir_recurse(dir, prot, &failed_dir); - if (ret != 0) { - EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); - xfree(failed_dir); - rettv->vval.v_number = FAIL; - return; - } else { - rettv->vval.v_number = OK; - return; - } + if (*path_tail((char_u *)dir) == NUL) { + // Remove trailing slashes. + *path_tail_with_sep((char_u *)dir) = NUL; + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[2].v_type != VAR_UNKNOWN) { + prot = tv_get_number_chk(&argvars[2], NULL); + if (prot == -1) { + return; + } + } + if (strcmp(tv_get_string(&argvars[1]), "p") == 0) { + char *failed_dir; + int ret = os_mkdir_recurse(dir, prot, &failed_dir); + if (ret != 0) { + EMSG3(_(e_mkdir), failed_dir, os_strerror(ret)); + xfree(failed_dir); + rettv->vval.v_number = FAIL; + return; + } else { + rettv->vval.v_number = OK; + return; } } - rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); } + rettv->vval.v_number = vim_mkdir_emsg(dir, prot); } /// "mode()" function @@ -13020,11 +13183,11 @@ static void f_printf(typval_T *argvars, typval_T *rettv, FunPtr fptr) did_emsg = false; char buf[NUMBUFLEN]; const char *fmt = tv_get_string_buf(&argvars[0], buf); - len = vim_vsnprintf(NULL, 0, fmt, dummy_ap, argvars + 1); + len = vim_vsnprintf_typval(NULL, 0, fmt, dummy_ap, argvars + 1); if (!did_emsg) { char *s = xmalloc(len + 1); rettv->vval.v_string = (char_u *)s; - (void)vim_vsnprintf(s, len + 1, fmt, dummy_ap, argvars + 1); + (void)vim_vsnprintf_typval(s, len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -14761,7 +14924,7 @@ static void set_qf_ll_list(win_T *wp, typval_T *args, typval_T *rettv) skip_args: if (!title) { - title = (wp ? "setloclist()" : "setqflist()"); + title = (wp ? ":setloclist()" : ":setqflist()"); } recursive++; @@ -14781,7 +14944,7 @@ static void f_setloclist(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = -1; - win = find_win_by_nr(&argvars[0], NULL); + win = find_win_by_nr_or_id(&argvars[0]); if (win != NULL) { set_qf_ll_list(win, &argvars[1], rettv); } @@ -14837,7 +15000,7 @@ static void f_setmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) // match from matchaddpos() for (i = 1; i < 9; i++) { - char buf[5]; + char buf[30]; // use 30 to avoid compiler warning snprintf(buf, sizeof(buf), "pos%d", i); dictitem_T *const pos_di = tv_dict_find(d, buf, -1); if (pos_di != NULL) { @@ -15087,6 +15250,60 @@ static void f_settabwinvar(typval_T *argvars, typval_T *rettv, FunPtr fptr) setwinvar(argvars, rettv, 1); } +// "settagstack()" function +static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + static char *e_invact2 = N_("E962: Invalid action: '%s'"); + win_T *wp; + dict_T *d; + int action = 'r'; + + rettv->vval.v_number = -1; + + // first argument: window number or id + wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { + return; + } + + // second argument: dict with items to set in the tag stack + if (argvars[1].v_type != VAR_DICT) { + EMSG(_(e_dictreq)); + return; + } + d = argvars[1].vval.v_dict; + if (d == NULL) { + return; + } + + // third argument: action - 'a' for append and 'r' for replace. + // default is to replace the stack. + if (argvars[2].v_type == VAR_UNKNOWN) { + action = 'r'; + } else if (argvars[2].v_type == VAR_STRING) { + const char *actstr; + actstr = tv_get_string_chk(&argvars[2]); + if (actstr == NULL) { + return; + } + if ((*actstr == 'r' || *actstr == 'a') && actstr[1] == NUL) { + action = *actstr; + } else { + EMSG2(_(e_invact2), actstr); + return; + } + } else { + EMSG(_(e_stringreq)); + return; + } + + if (set_tagstack(wp, d, action) == OK) { + rettv->vval.v_number = 0; + } else { + EMSG(_(e_listreq)); + } +} + /* * "setwinvar()" function */ @@ -16306,6 +16523,27 @@ static void f_substitute(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +/// "swapinfo(swap_filename)" function +static void f_swapinfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + tv_dict_alloc_ret(rettv); + get_b0_dict(tv_get_string(argvars), rettv->vval.v_dict); +} + +/// "swapname(expr)" function +static void f_swapname(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + rettv->v_type = VAR_STRING; + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL + || buf->b_ml.ml_mfp == NULL + || buf->b_ml.ml_mfp->mf_fname == NULL) { + rettv->vval.v_string = NULL; + } else { + rettv->vval.v_string = vim_strsave(buf->b_ml.ml_mfp->mf_fname); + } +} + /// "synID(lnum, col, trans)" function static void f_synID(typval_T *argvars, typval_T *rettv, FunPtr fptr) { @@ -16631,7 +16869,6 @@ static void f_tabpagebuflist(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } - /* * "tabpagenr()" function */ @@ -16668,6 +16905,7 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) twin = (tp == curtab) ? curwin : tp->tp_curwin; if (argvar->v_type != VAR_UNKNOWN) { + bool invalid_arg = false; const char *const arg = tv_get_string_chk(argvar); if (arg == NULL) { nr = 0; // Type error; errmsg already given. @@ -16679,6 +16917,31 @@ static int get_winnr(tabpage_T *tp, typval_T *argvar) nr = 0; } } else { + // Extract the window count (if specified). e.g. winnr('3j') + char_u *endp; + long count = strtol((char *)arg, (char **)&endp, 10); + if (count <= 0) { + // if count is not specified, default to 1 + count = 1; + } + if (endp != NULL && *endp != '\0') { + if (strequal((char *)endp, "j")) { + twin = win_vert_neighbor(tp, twin, false, count); + } else if (strequal((char *)endp, "k")) { + twin = win_vert_neighbor(tp, twin, true, count); + } else if (strequal((char *)endp, "h")) { + twin = win_horz_neighbor(tp, twin, true, count); + } else if (strequal((char *)endp, "l")) { + twin = win_horz_neighbor(tp, twin, false, count); + } else { + invalid_arg = true; + } + } else { + invalid_arg = true; + } + } + + if (invalid_arg) { EMSG2(_(e_invexpr2), arg); nr = 0; } @@ -16712,7 +16975,6 @@ static void f_tabpagewinnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = nr; } - /* * "tagfiles()" function */ @@ -17075,6 +17337,7 @@ static void f_timer_start(typval_T *argvars, typval_T *rettv, FunPtr fptr) timer->refcount = 1; timer->stopped = false; timer->paused = false; + timer->emsg_count = 0; timer->repeat_count = repeat; timer->timeout = timeout; timer->timer_id = last_timer_id++; @@ -17117,6 +17380,9 @@ static void f_timer_stopall(typval_T *argvars, typval_T *unused, FunPtr fptr) static void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; + int save_did_emsg = did_emsg; + int save_called_emsg = called_emsg; + if (timer->stopped || timer->paused) { return; } @@ -17131,8 +17397,24 @@ static void timer_due_cb(TimeWatcher *tw, void *data) argv[0].v_type = VAR_NUMBER; argv[0].vval.v_number = timer->timer_id; typval_T rettv = TV_INITIAL_VALUE; + called_emsg = false; callback_call(&timer->callback, 1, argv, &rettv); + + // Handle error message + if (called_emsg && did_emsg) { + timer->emsg_count++; + if (current_exception != NULL) { + discard_current_exception(); + } + } + did_emsg = save_did_emsg; + called_emsg = save_called_emsg; + + if (timer->emsg_count >= 3) { + timer_stop(timer); + } + tv_clear(&rettv); if (!timer->stopped && timer->timeout == 0) { @@ -17419,7 +17701,7 @@ static void f_undofile(typval_T *argvars, typval_T *rettv, FunPtr fptr) // If there is no file name there will be no undo file. rettv->vval.v_string = NULL; } else { - char *ffname = FullName_save(fname, false); + char *ffname = FullName_save(fname, true); if (ffname != NULL) { rettv->vval.v_string = (char_u *)u_get_undo_file_name(ffname, false); @@ -17532,18 +17814,15 @@ static void f_win_id2win(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = win_id2win(argvars); } -/* - * "winbufnr(nr)" function - */ +/// "winbufnr(nr)" function static void f_winbufnr(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = wp->w_buffer->b_fnum; + } } /* @@ -17555,18 +17834,15 @@ static void f_wincol(typval_T *argvars, typval_T *rettv, FunPtr fptr) rettv->vval.v_number = curwin->w_wcol + 1; } -/* - * "winheight(nr)" function - */ +/// "winheight(nr)" function static void f_winheight(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = wp->w_height; + } } /* @@ -17830,18 +18106,15 @@ static char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) return ret; } -/* - * "winwidth(nr)" function - */ +/// "winwidth(nr)" function static void f_winwidth(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - win_T *wp; - - wp = find_win_by_nr(&argvars[0], NULL); - if (wp == NULL) + win_T *wp = find_win_by_nr_or_id(&argvars[0]); + if (wp == NULL) { rettv->vval.v_number = -1; - else + } else { rettv->vval.v_number = wp->w_width; + } } /// "wordcount()" function @@ -18053,7 +18326,7 @@ pos_T *var2fpos(const typval_T *const tv, const int dollar_lnum, * Return FAIL when conversion is not possible, doesn't check the position for * validity. */ -static int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) { list_T *l; long i = 0; @@ -19570,16 +19843,15 @@ void ex_echo(exarg_T *eap) { char_u *arg = eap->arg; typval_T rettv; - bool needclr = true; bool atstart = true; const int did_emsg_before = did_emsg; if (eap->skip) ++emsg_skip; while (*arg != NUL && *arg != '|' && *arg != '\n' && !got_int) { - /* If eval1() causes an error message the text from the command may - * still need to be cleared. E.g., "echo 22,44". */ - need_clr_eos = needclr; + // If eval1() causes an error message the text from the command may + // still need to be cleared. E.g., "echo 22,44". + need_clr_eos = true; { char_u *p = arg; @@ -19623,14 +19895,14 @@ void ex_echo(exarg_T *eap) } eap->nextcmd = check_nextcmd(arg); - if (eap->skip) - --emsg_skip; - else { - /* remove text that may still be there from the command */ - if (needclr) - msg_clr_eos(); - if (eap->cmdidx == CMD_echo) + if (eap->skip) { + emsg_skip--; + } else { + // remove text that may still be there from the command + msg_clr_eos(); + if (eap->cmdidx == CMD_echo) { msg_end(); + } } } @@ -20348,6 +20620,9 @@ void ex_function(exarg_T *eap) if (prof_def_func()) func_do_profile(fp); fp->uf_varargs = varargs; + if (sandbox) { + flags |= FC_SANDBOX; + } fp->uf_flags = flags; fp->uf_calls = 0; fp->uf_script_ID = current_SID; @@ -21338,6 +21613,7 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, char_u *save_sourcing_name; linenr_T save_sourcing_lnum; scid_T save_current_SID; + bool using_sandbox = false; funccall_T *fc; int save_did_emsg; static int depth = 0; @@ -21495,6 +21771,12 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, save_sourcing_name = sourcing_name; save_sourcing_lnum = sourcing_lnum; sourcing_lnum = 1; + + if (fp->uf_flags & FC_SANDBOX) { + using_sandbox = true; + sandbox++; + } + // need space for new sourcing_name: // * save_sourcing_name // * "["number"].." or "function " @@ -21655,6 +21937,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, if (do_profiling_yes) { script_prof_restore(&wait_start); } + if (using_sandbox) { + sandbox--; + } if (p_verbose >= 12 && sourcing_name != NULL) { ++no_wait_return; |