diff options
Diffstat (limited to 'src/nvim/eval.c')
| -rw-r--r-- | src/nvim/eval.c | 1952 |
1 files changed, 795 insertions, 1157 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index b97282dd1a..3e855ece15 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -6,6 +6,7 @@ */ #include <math.h> +#include <stdlib.h> #include "auto/config.h" @@ -27,20 +28,18 @@ #include "nvim/eval/typval.h" #include "nvim/eval/userfunc.h" #include "nvim/ex_cmds2.h" -#include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" #include "nvim/ex_session.h" #include "nvim/fileio.h" #include "nvim/getchar.h" +#include "nvim/highlight_group.h" #include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/memline.h" -#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/os/input.h" -#include "nvim/os/os.h" #include "nvim/os/shell.h" #include "nvim/path.h" #include "nvim/quickfix.h" @@ -67,9 +66,9 @@ static char *e_illvar = N_("E461: Illegal variable name: %s"); static char *e_cannot_mod = N_("E995: Cannot modify existing variable"); static char *e_nowhitespace = N_("E274: No white space allowed before parenthesis"); -static char *e_invalwindow = N_("E957: Invalid window number"); static char *e_lock_unlock = N_("E940: Cannot lock or unlock variable %s"); static char *e_write2 = N_("E80: Error while writing: %s"); +static char *e_string_list_or_blob_required = N_("E1098: String, List or Blob required"); // TODO(ZyX-I): move to eval/executor static char *e_letwrong = N_("E734: Wrong variable type for %s="); @@ -113,9 +112,11 @@ typedef struct { int fi_semicolon; // TRUE if ending in '; var]' int fi_varcount; // nr of variables in the list listwatch_T fi_lw; // keep an eye on the item used. - list_T *fi_list; // list being used + list_T *fi_list; // list being used int fi_bi; // index of blob blob_T *fi_blob; // blob being used + char_u *fi_string; // copy of string being used + int fi_byte_idx; // byte index in fi_string } forinfo_T; // values for vv_flags: @@ -134,13 +135,15 @@ typedef struct { .vv_flags = flags, \ } +#define VIMVAR_KEY_LEN 16 // Maximum length of the key of v:variables + // Array to hold the value of v: variables. // The value is in a dictitem, so that it can also be used in the v: scope. // The reason to use this table anyway is for very quick access to the // variables with the VV_ defines. static struct vimvar { char *vv_name; ///< Name of the variable, without v:. - TV_DICTITEM_STRUCT(17) vv_di; ///< Value and name for key (max 16 chars). + TV_DICTITEM_STRUCT(VIMVAR_KEY_LEN + 1) vv_di; ///< Value and name for key (max 16 chars). char vv_flags; ///< Flags: #VV_COMPAT, #VV_RO, #VV_RO_SBX. } vimvars[] = { @@ -150,100 +153,103 @@ static struct vimvar { // VV_SEND_SERVER "servername" // VV_REG "register" // VV_OP "operator" - VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), - VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), - VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), - VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), - VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), - VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), - VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), - VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), - VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), - VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), - VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), - VV(VV_FNAME, "fname", VAR_STRING, VV_RO), - VV(VV_LANG, "lang", VAR_STRING, VV_RO), - VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), - VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), - VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), - VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), - VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), - VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), - VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), - VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), - VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), - VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), - VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), - VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), - VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), - VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), - VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), - VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), - VV(VV_REG, "register", VAR_STRING, VV_RO), - VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), - VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), - VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), - VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), - VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), - VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), - VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), - VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), - VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), - VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), - VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), - VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), - VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), - VV(VV_CHAR, "char", VAR_STRING, 0), - VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), - VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), - VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), - VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), - VV(VV_OP, "operator", VAR_STRING, VV_RO), - VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), - VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), - VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), - VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), - VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), - VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), - VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), - VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), - VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), - VV(VV_ERRORS, "errors", VAR_LIST, 0), - VV(VV_FALSE, "false", VAR_BOOL, VV_RO), - VV(VV_TRUE, "true", VAR_BOOL, VV_RO), - VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), - VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), - VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), - VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), - VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), - VV(VV_TESTING, "testing", VAR_NUMBER, 0), - VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), - VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), - VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), - VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), - VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), - VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), - VV(VV_EVENT, "event", VAR_DICT, VV_RO), - VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), - VV(VV_ARGV, "argv", VAR_LIST, VV_RO), - VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), - VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_COUNT, "count", VAR_NUMBER, VV_RO), + VV(VV_COUNT1, "count1", VAR_NUMBER, VV_RO), + VV(VV_PREVCOUNT, "prevcount", VAR_NUMBER, VV_RO), + VV(VV_ERRMSG, "errmsg", VAR_STRING, 0), + VV(VV_WARNINGMSG, "warningmsg", VAR_STRING, 0), + VV(VV_STATUSMSG, "statusmsg", VAR_STRING, 0), + VV(VV_SHELL_ERROR, "shell_error", VAR_NUMBER, VV_RO), + VV(VV_THIS_SESSION, "this_session", VAR_STRING, 0), + VV(VV_VERSION, "version", VAR_NUMBER, VV_COMPAT+VV_RO), + VV(VV_LNUM, "lnum", VAR_NUMBER, VV_RO_SBX), + VV(VV_TERMRESPONSE, "termresponse", VAR_STRING, VV_RO), + VV(VV_FNAME, "fname", VAR_STRING, VV_RO), + VV(VV_LANG, "lang", VAR_STRING, VV_RO), + VV(VV_LC_TIME, "lc_time", VAR_STRING, VV_RO), + VV(VV_CTYPE, "ctype", VAR_STRING, VV_RO), + VV(VV_CC_FROM, "charconvert_from", VAR_STRING, VV_RO), + VV(VV_CC_TO, "charconvert_to", VAR_STRING, VV_RO), + VV(VV_FNAME_IN, "fname_in", VAR_STRING, VV_RO), + VV(VV_FNAME_OUT, "fname_out", VAR_STRING, VV_RO), + VV(VV_FNAME_NEW, "fname_new", VAR_STRING, VV_RO), + VV(VV_FNAME_DIFF, "fname_diff", VAR_STRING, VV_RO), + VV(VV_CMDARG, "cmdarg", VAR_STRING, VV_RO), + VV(VV_FOLDSTART, "foldstart", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDEND, "foldend", VAR_NUMBER, VV_RO_SBX), + VV(VV_FOLDDASHES, "folddashes", VAR_STRING, VV_RO_SBX), + VV(VV_FOLDLEVEL, "foldlevel", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGNAME, "progname", VAR_STRING, VV_RO), + VV(VV_SEND_SERVER, "servername", VAR_STRING, VV_RO), + VV(VV_DYING, "dying", VAR_NUMBER, VV_RO), + VV(VV_EXCEPTION, "exception", VAR_STRING, VV_RO), + VV(VV_THROWPOINT, "throwpoint", VAR_STRING, VV_RO), + VV(VV_REG, "register", VAR_STRING, VV_RO), + VV(VV_CMDBANG, "cmdbang", VAR_NUMBER, VV_RO), + VV(VV_INSERTMODE, "insertmode", VAR_STRING, VV_RO), + VV(VV_VAL, "val", VAR_UNKNOWN, VV_RO), + VV(VV_KEY, "key", VAR_UNKNOWN, VV_RO), + VV(VV_PROFILING, "profiling", VAR_NUMBER, VV_RO), + VV(VV_FCS_REASON, "fcs_reason", VAR_STRING, VV_RO), + VV(VV_FCS_CHOICE, "fcs_choice", VAR_STRING, 0), + VV(VV_BEVAL_BUFNR, "beval_bufnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINNR, "beval_winnr", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_WINID, "beval_winid", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_LNUM, "beval_lnum", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_COL, "beval_col", VAR_NUMBER, VV_RO), + VV(VV_BEVAL_TEXT, "beval_text", VAR_STRING, VV_RO), + VV(VV_SCROLLSTART, "scrollstart", VAR_STRING, 0), + VV(VV_SWAPNAME, "swapname", VAR_STRING, VV_RO), + VV(VV_SWAPCHOICE, "swapchoice", VAR_STRING, 0), + VV(VV_SWAPCOMMAND, "swapcommand", VAR_STRING, VV_RO), + VV(VV_CHAR, "char", VAR_STRING, 0), + VV(VV_MOUSE_WIN, "mouse_win", VAR_NUMBER, 0), + VV(VV_MOUSE_WINID, "mouse_winid", VAR_NUMBER, 0), + VV(VV_MOUSE_LNUM, "mouse_lnum", VAR_NUMBER, 0), + VV(VV_MOUSE_COL, "mouse_col", VAR_NUMBER, 0), + VV(VV_OP, "operator", VAR_STRING, VV_RO), + VV(VV_SEARCHFORWARD, "searchforward", VAR_NUMBER, 0), + VV(VV_HLSEARCH, "hlsearch", VAR_NUMBER, 0), + VV(VV_OLDFILES, "oldfiles", VAR_LIST, 0), + VV(VV_WINDOWID, "windowid", VAR_NUMBER, VV_RO_SBX), + VV(VV_PROGPATH, "progpath", VAR_STRING, VV_RO), + VV(VV_COMPLETED_ITEM, "completed_item", VAR_DICT, VV_RO), + VV(VV_OPTION_NEW, "option_new", VAR_STRING, VV_RO), + VV(VV_OPTION_OLD, "option_old", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDLOCAL, "option_oldlocal", VAR_STRING, VV_RO), + VV(VV_OPTION_OLDGLOBAL, "option_oldglobal", VAR_STRING, VV_RO), + VV(VV_OPTION_COMMAND, "option_command", VAR_STRING, VV_RO), + VV(VV_OPTION_TYPE, "option_type", VAR_STRING, VV_RO), + VV(VV_ERRORS, "errors", VAR_LIST, 0), + VV(VV_FALSE, "false", VAR_BOOL, VV_RO), + VV(VV_TRUE, "true", VAR_BOOL, VV_RO), + VV(VV_NULL, "null", VAR_SPECIAL, VV_RO), + VV(VV_NUMBERMAX, "numbermax", VAR_NUMBER, VV_RO), + VV(VV_NUMBERMIN, "numbermin", VAR_NUMBER, VV_RO), + VV(VV_NUMBERSIZE, "numbersize", VAR_NUMBER, VV_RO), + VV(VV_VIM_DID_ENTER, "vim_did_enter", VAR_NUMBER, VV_RO), + VV(VV_TESTING, "testing", VAR_NUMBER, 0), + VV(VV_TYPE_NUMBER, "t_number", VAR_NUMBER, VV_RO), + VV(VV_TYPE_STRING, "t_string", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FUNC, "t_func", VAR_NUMBER, VV_RO), + VV(VV_TYPE_LIST, "t_list", VAR_NUMBER, VV_RO), + VV(VV_TYPE_DICT, "t_dict", VAR_NUMBER, VV_RO), + VV(VV_TYPE_FLOAT, "t_float", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BOOL, "t_bool", VAR_NUMBER, VV_RO), + VV(VV_TYPE_BLOB, "t_blob", VAR_NUMBER, VV_RO), + VV(VV_EVENT, "event", VAR_DICT, VV_RO), + VV(VV_ECHOSPACE, "echospace", VAR_NUMBER, VV_RO), + VV(VV_ARGV, "argv", VAR_LIST, VV_RO), + VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), + VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), // Neovim - VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), - VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), - VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), - VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), - VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), - VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), - VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), + VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), + VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), + VV(VV__NULL_STRING, "_null_string", VAR_STRING, VV_RO), + VV(VV__NULL_LIST, "_null_list", VAR_LIST, VV_RO), + VV(VV__NULL_DICT, "_null_dict", VAR_DICT, VV_RO), + VV(VV__NULL_BLOB, "_null_blob", VAR_BLOB, VV_RO), + VV(VV_LUA, "lua", VAR_PARTIAL, VV_RO), }; #undef VV @@ -298,7 +304,32 @@ const list_T *eval_msgpack_type_lists[] = { [kMPExt] = NULL, }; -// Return "n1" divided by "n2", taking care of dividing by zero. +dict_T *get_v_event(save_v_event_T *sve) +{ + dict_T *v_event = get_vim_var_dict(VV_EVENT); + + if (v_event->dv_hashtab.ht_used > 0) { + // recursive use of v:event, save, make empty and restore later + sve->sve_did_save = true; + sve->sve_hashtab = v_event->dv_hashtab; + hash_init(&v_event->dv_hashtab); + } else { + sve->sve_did_save = false; + } + return v_event; +} + +void restore_v_event(dict_T *v_event, save_v_event_T *sve) +{ + tv_dict_free_contents(v_event); + if (sve->sve_did_save) { + v_event->dv_hashtab = sve->sve_hashtab; + } else { + hash_init(&v_event->dv_hashtab); + } +} + +/// @return "n1" divided by "n2", taking care of dividing by zero. varnumber_T num_divide(varnumber_T n1, varnumber_T n2) FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { @@ -319,7 +350,7 @@ varnumber_T num_divide(varnumber_T n1, varnumber_T n2) return result; } -// Return "n1" modulus "n2", taking care of dividing by zero. +/// @return "n1" modulus "n2", taking care of dividing by zero. varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) FUNC_ATTR_CONST FUNC_ATTR_WARN_UNUSED_RESULT { @@ -327,9 +358,7 @@ varnumber_T num_modulus(varnumber_T n1, varnumber_T n2) return (n2 == 0) ? 0 : (n1 % n2); } -/* - * Initialize the global and v: variables. - */ +/// Initialize the global and v: variables. void eval_init(void) { vimvars[VV_VERSION].vv_nr = VIM_VERSION_100; @@ -344,7 +373,7 @@ void eval_init(void) for (size_t i = 0; i < ARRAY_SIZE(vimvars); i++) { p = &vimvars[i]; - assert(STRLEN(p->vv_name) <= 16); + assert(STRLEN(p->vv_name) <= VIMVAR_KEY_LEN); STRCPY(p->vv_di.di_key, p->vv_name); if (p->vv_flags & VV_RO) { p->vv_di.di_flags = DI_FLAGS_RO | DI_FLAGS_FIX; @@ -469,10 +498,8 @@ void eval_clear(void) #endif -/* - * Set an internal variable to a string value. Creates the variable if it does - * not already exist. - */ +/// Set an internal variable to a string value. Creates the variable if it does +/// not already exist. void set_internal_string_var(const char *name, char_u *value) FUNC_ATTR_NONNULL_ARG(1) { @@ -490,9 +517,10 @@ static char_u *redir_endp = NULL; static char_u *redir_varname = NULL; /// Start recording command output to a variable -/// Returns OK if successfully completed the setup. FAIL otherwise. /// /// @param append append to an existing variable +/// +/// @return OK if successfully completed the setup. FAIL otherwise. int var_redir_start(char_u *name, int append) { int save_emsg; @@ -553,15 +581,13 @@ int var_redir_start(char_u *name, int append) return OK; } -/* - * Append "value[value_len]" to the variable set by var_redir_start(). - * The actual appending is postponed until redirection ends, because the value - * appended may in fact be the string we write to, changing it may cause freed - * memory to be used: - * :redir => foo - * :let foo - * :redir END - */ +/// Append "value[value_len]" to the variable set by var_redir_start(). +/// The actual appending is postponed until redirection ends, because the value +/// appended may in fact be the string we write to, changing it may cause freed +/// memory to be used: +/// :redir => foo +/// :let foo +/// :redir END void var_redir_str(char_u *value, int value_len) { int len; @@ -581,10 +607,8 @@ void var_redir_str(char_u *value, int value_len) redir_ga.ga_len += len; } -/* - * Stop redirecting command output to a variable. - * Frees the allocated memory. - */ +/// Stop redirecting command output to a variable. +/// Frees the allocated memory. void var_redir_stop(void) { typval_T tv; @@ -711,7 +735,7 @@ int eval_to_bool(char_u *arg, bool *error, char_u **nextcmd, int skip) return retval; } -// Call eval1() and give an error message if not done at a lower level. +/// Call eval1() and give an error message if not done at a lower level. static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate) FUNC_ATTR_NONNULL_ARG(1, 2) { @@ -734,6 +758,15 @@ static int eval1_emsg(char_u **arg, typval_T *rettv, bool evaluate) return ret; } +/// @return whether a typval is a valid expression to pass to eval_expr_typval() +/// or eval_expr_to_bool(). An empty string returns false; +bool eval_expr_valid_arg(const typval_T *const tv) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_CONST +{ + return tv->v_type != VAR_UNKNOWN + && (tv->v_type != VAR_STRING || (tv->vval.v_string != NULL && *tv->vval.v_string != NUL)); +} + int eval_expr_typval(const typval_T *expr, typval_T *argv, int argc, typval_T *rettv) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { @@ -825,10 +858,9 @@ char *eval_to_string_skip(const char *arg, const char **nextcmd, const bool skip return retval; } -/* - * Skip over an expression at "*pp". - * Return FAIL for an error, OK otherwise. - */ +/// Skip over an expression at "*pp". +/// +/// @return FAIL for an error, OK otherwise. int skip_expr(char_u **pp) { typval_T rettv; @@ -875,10 +907,10 @@ char_u *eval_to_string(char_u *arg, char_u **nextcmd, bool convert) return (char_u *)retval; } -/* - * Call eval_to_string() without using current local variables and using - * textlock. When "use_sandbox" is TRUE use the sandbox. - */ +/// Call eval_to_string() without using current local variables and using +/// textlock. +/// +/// @param use_sandbox when TRUE, use the sandbox. char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox) { char_u *retval; @@ -898,11 +930,10 @@ char_u *eval_to_string_safe(char_u *arg, char_u **nextcmd, int use_sandbox) return retval; } -/* - * Top level evaluation function, returning a number. - * Evaluates "expr" silently. - * Returns -1 for an error. - */ +/// Top level evaluation function, returning a number. +/// Evaluates "expr" silently. +/// +/// @return -1 for an error. varnumber_T eval_to_number(char_u *expr) { typval_T rettv; @@ -922,9 +953,10 @@ varnumber_T eval_to_number(char_u *expr) return retval; } -// Top level evaluation function. -// Returns an allocated typval_T with the result. -// Returns NULL when there is an error. +/// Top level evaluation function. +/// +/// @return an allocated typval_T with the result or +/// NULL when there is an error. typval_T *eval_expr(char_u *arg) { typval_T *tv = xmalloc(sizeof(*tv)); @@ -934,11 +966,9 @@ typval_T *eval_expr(char_u *arg) return tv; } -/* - * Prepare v: variable "idx" to be used. - * Save the current typeval in "save_tv". - * When not used yet add the variable to the v: hashtable. - */ +/// Prepare v: variable "idx" to be used. +/// Save the current typeval in "save_tv". +/// When not used yet add the variable to the v: hashtable. void prepare_vimvar(int idx, typval_T *save_tv) { *save_tv = vimvars[idx].vv_tv; @@ -947,10 +977,8 @@ void prepare_vimvar(int idx, typval_T *save_tv) } } -/* - * Restore v: variable "idx" to typeval "save_tv". - * When no longer defined, remove the variable from the v: hashtable. - */ +/// Restore v: variable "idx" to typeval "save_tv". +/// When no longer defined, remove the variable from the v: hashtable. void restore_vimvar(int idx, typval_T *save_tv) { hashitem_T *hi; @@ -977,11 +1005,10 @@ void find_win_for_curbuf(void) } } -/* - * Evaluate an expression to a list with suggestions. - * For the "expr:" part of 'spellsuggest'. - * Returns NULL when there is an error. - */ +/// Evaluate an expression to a list with suggestions. +/// For the "expr:" part of 'spellsuggest'. +/// +/// @return NULL when there is an error. list_T *eval_spell_expr(char_u *badword, char_u *expr) { typval_T save_val; @@ -1045,7 +1072,7 @@ int get_spellword(list_T *const list, const char **ret_word) // Uses argv[0] to argv[argc-1] for the function arguments. argv[argc] // should have type VAR_UNKNOWN. // -// Return OK or FAIL. +// @return OK or FAIL. int call_vim_function(const char_u *func, int argc, typval_T *argv, typval_T *rettv) FUNC_ATTR_NONNULL_ALL { @@ -1183,10 +1210,8 @@ void prof_child_exit(proftime_T *tm) } -/* - * Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding - * it in "*cp". Doesn't give error messages. - */ +/// Evaluate 'foldexpr'. Returns the foldlevel, and any character preceding +/// it in "*cp". Doesn't give error messages. int eval_foldexpr(char_u *arg, int *cp) { typval_T tv; @@ -1227,26 +1252,27 @@ int eval_foldexpr(char_u *arg, int *cp) return (int)retval; } -// ":cons[t] var = expr1" define constant -// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list -// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list +/// ":cons[t] var = expr1" define constant +/// ":cons[t] [name1, name2, ...] = expr1" define constants unpacking list +/// ":cons[t] [name, ..., ; lastname] = expr" define constants unpacking list void ex_const(exarg_T *eap) { ex_let_const(eap, true); } -// Get a list of lines from a HERE document. The here document is a list of -// lines surrounded by a marker. -// cmd << {marker} -// {line1} -// {line2} -// .... -// {marker} -// -// The {marker} is a string. If the optional 'trim' word is supplied before the -// marker, then the leading indentation before the lines (matching the -// indentation in the 'cmd' line) is stripped. -// Returns a List with {lines} or NULL. +/// Get a list of lines from a HERE document. The here document is a list of +/// lines surrounded by a marker. +/// cmd << {marker} +/// {line1} +/// {line2} +/// .... +/// {marker} +/// +/// The {marker} is a string. If the optional 'trim' word is supplied before the +/// marker, then the leading indentation before the lines (matching the +/// indentation in the 'cmd' line) is stripped. +/// +/// @return a List with {lines} or NULL. static list_T *heredoc_get(exarg_T *eap, char_u *cmd) { char_u *marker; @@ -1344,18 +1370,18 @@ static list_T *heredoc_get(exarg_T *eap, char_u *cmd) return l; } -// ":let" list all variable values -// ":let var1 var2" list variable values -// ":let var = expr" assignment command. -// ":let var += expr" assignment command. -// ":let var -= expr" assignment command. -// ":let var *= expr" assignment command. -// ":let var /= expr" assignment command. -// ":let var %= expr" assignment command. -// ":let var .= expr" assignment command. -// ":let var ..= expr" assignment command. -// ":let [var1, var2] = expr" unpack list. -// ":let [name, ..., ; lastname] = expr" unpack list. +/// ":let" list all variable values +/// ":let var1 var2" list variable values +/// ":let var = expr" assignment command. +/// ":let var += expr" assignment command. +/// ":let var -= expr" assignment command. +/// ":let var *= expr" assignment command. +/// ":let var /= expr" assignment command. +/// ":let var %= expr" assignment command. +/// ":let var .= expr" assignment command. +/// ":let var ..= expr" assignment command. +/// ":let [var1, var2] = expr" unpack list. +/// ":let [name, ..., ; lastname] = expr" unpack list. void ex_let(exarg_T *eap) { ex_let_const(eap, false); @@ -1536,13 +1562,12 @@ static int ex_let_vars(char_u *arg_start, typval_T *tv, int copy, int semicolon, return OK; } -/* - * Skip over assignable variable "var" or list of variables "[var, var]". - * Used for ":let varvar = expr" and ":for varvar in expr". - * For "[var, var]" increment "*var_count" for each variable. - * for "[var, var; var]" set "semicolon". - * Return NULL for an error. - */ +/// Skip over assignable variable "var" or list of variables "[var, var]". +/// Used for ":let varvar = expr" and ":for varvar in expr". +/// For "[var, var]" increment "*var_count" for each variable. +/// for "[var, var; var]" set "semicolon". +/// +/// @return NULL for an error. static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semicolon) { const char_u *p; @@ -1580,10 +1605,8 @@ static const char_u *skip_var_list(const char_u *arg, int *var_count, int *semic } } -/* - * Skip one (assignable) variable name, including @r, $VAR, &option, d.key, - * l[idx]. - */ +/// Skip one (assignable) variable name, including @r, $VAR, &option, d.key, +/// l[idx]. static const char_u *skip_var_one(const char_u *arg) { if (*arg == '@' && arg[1] != NUL) { @@ -1593,10 +1616,9 @@ static const char_u *skip_var_one(const char_u *arg) NULL, NULL, FNE_INCL_BR | FNE_CHECK_START); } -/* - * List variables for hashtab "ht" with prefix "prefix". - * If "empty" is TRUE also list NULL strings as empty strings. - */ +/// List variables for hashtab "ht" with prefix "prefix". +/// +/// @param empty if TRUE also list NULL strings as empty strings. void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *first) { hashitem_T *hi; @@ -1625,47 +1647,37 @@ void list_hashtable_vars(hashtab_T *ht, const char *prefix, int empty, int *firs } } -/* - * List global variables. - */ +/// List global variables. static void list_glob_vars(int *first) { list_hashtable_vars(&globvarht, "", true, first); } -/* - * List buffer variables. - */ +/// List buffer variables. static void list_buf_vars(int *first) { list_hashtable_vars(&curbuf->b_vars->dv_hashtab, "b:", true, first); } -/* - * List window variables. - */ +/// List window variables. static void list_win_vars(int *first) { list_hashtable_vars(&curwin->w_vars->dv_hashtab, "w:", true, first); } -/* - * List tab page variables. - */ +/// List tab page variables. static void list_tab_vars(int *first) { list_hashtable_vars(&curtab->tp_vars->dv_hashtab, "t:", true, first); } -/* - * List Vim variables. - */ +/// List Vim variables. static void list_vim_vars(int *first) { list_hashtable_vars(&vimvarht, "v:", false, first); } -// List script-local variables, if there is a script. +/// List script-local variables, if there is a script. static void list_script_vars(int *first) { if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= ga_scripts.ga_len) { @@ -1673,9 +1685,7 @@ static void list_script_vars(int *first) } } -/* - * List variables in "arg". - */ +/// List variables in "arg". static const char *list_arg_vars(exarg_T *eap, const char *arg, int *first) { int error = FALSE; @@ -2338,9 +2348,7 @@ char_u *get_lval(char_u *const name, typval_T *const rettv, lval_T *const lp, co // TODO(ZyX-I): move to eval/executor -/* - * Clear lval "lp" that was filled by get_lval(). - */ +/// Clear lval "lp" that was filled by get_lval(). void clear_lval(lval_T *lp) { xfree(lp->ll_exp_name); @@ -2349,12 +2357,11 @@ void clear_lval(lval_T *lp) // TODO(ZyX-I): move to eval/executor -/* - * Set a variable that was parsed by get_lval() to "rettv". - * "endp" points to just after the parsed name. - * "op" is NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", - * "%" for "%=", "." for ".=" or "=" for "=". - */ +/// Set a variable that was parsed by get_lval() to "rettv". +/// +/// @param endp points to just after the parsed name. +/// @param op NULL, "+" for "+=", "-" for "-=", "*" for "*=", "/" for "/=", +/// "%" for "%=", "." for ".=" or "=" for "=". static void set_var_lval(lval_T *lp, char_u *endp, typval_T *rettv, int copy, const bool is_const, const char *op) { @@ -2557,12 +2564,12 @@ notify: // TODO(ZyX-I): move to eval/ex_cmds -/* - * Evaluate the expression used in a ":for var in expr" command. - * "arg" points to "var". - * Set "*errp" to TRUE for an error, FALSE otherwise; - * Return a pointer that holds the info. Null when there is an error. - */ +/// Evaluate the expression used in a ":for var in expr" command. +/// "arg" points to "var". +/// +/// @param[out] *errp set to TRUE for an error, FALSE otherwise; +/// +/// @return a pointer that holds the info. Null when there is an error. void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) { forinfo_T *fi = xcalloc(1, sizeof(forinfo_T)); @@ -2612,8 +2619,15 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) fi->fi_blob = btv.vval.v_blob; } tv_clear(&tv); + } else if (tv.v_type == VAR_STRING) { + fi->fi_byte_idx = 0; + fi->fi_string = tv.vval.v_string; + tv.vval.v_string = NULL; + if (fi->fi_string == NULL) { + fi->fi_string = vim_strsave((char_u *)""); + } } else { - emsg(_(e_listblobreq)); + emsg(_(e_string_list_or_blob_required)); tv_clear(&tv); } } @@ -2627,12 +2641,11 @@ void *eval_for_line(const char_u *arg, bool *errp, char_u **nextcmdp, int skip) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Use the first item in a ":for" list. Advance to the next. - * Assign the values to the variable (list). "arg" points to the first one. - * Return TRUE when a valid item was found, FALSE when at end of list or - * something wrong. - */ +/// Use the first item in a ":for" list. Advance to the next. +/// Assign the values to the variable (list). "arg" points to the first one. +/// +/// @return true when a valid item was found, false when at end of list or +/// something wrong. bool next_for_item(void *fi_void, char_u *arg) { forinfo_T *fi = (forinfo_T *)fi_void; @@ -2650,6 +2663,22 @@ bool next_for_item(void *fi_void, char_u *arg) fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; } + if (fi->fi_string != NULL) { + const int len = utfc_ptr2len(fi->fi_string + fi->fi_byte_idx); + if (len == 0) { + return false; + } + typval_T tv; + tv.v_type = VAR_STRING; + tv.v_lock = VAR_FIXED; + tv.vval.v_string = vim_strnsave(fi->fi_string + fi->fi_byte_idx, len); + fi->fi_byte_idx += len; + const int result + = ex_let_vars(arg, &tv, true, fi->fi_semicolon, fi->fi_varcount, false, NULL) == OK; + xfree(tv.vval.v_string); + return result; + } + listitem_T *item = fi->fi_lw.lw_item; if (item == NULL) { return false; @@ -2662,19 +2691,21 @@ bool next_for_item(void *fi_void, char_u *arg) // TODO(ZyX-I): move to eval/ex_cmds -/* - * Free the structure used to store info used by ":for". - */ +/// Free the structure used to store info used by ":for". void free_for_info(void *fi_void) { forinfo_T *fi = (forinfo_T *)fi_void; - if (fi != NULL && fi->fi_list != NULL) { + if (fi == NULL) { + return; + } + if (fi->fi_list != NULL) { tv_list_watch_remove(fi->fi_list, &fi->fi_lw); tv_list_unref(fi->fi_list); - } - if (fi != NULL && fi->fi_blob != NULL) { + } else if (fi->fi_blob != NULL) { tv_blob_unref(fi->fi_blob); + } else { + xfree(fi->fi_string); } xfree(fi); } @@ -3109,9 +3140,7 @@ static int do_lock_var(lval_T *lp, char_u *name_end FUNC_ATTR_UNUSED, exarg_T *e return ret; } -/* - * Delete all "menutrans_" variables. - */ +/// Delete all "menutrans_" variables. void del_menutrans_vars(void) { hash_lock(&globvarht); @@ -3133,9 +3162,7 @@ void del_menutrans_vars(void) static char_u *varnamebuf = NULL; static size_t varnamebuflen = 0; -/* - * Function to concatenate a prefix and a variable name. - */ +/// Function to concatenate a prefix and a variable name. char_u *cat_prefix_varname(int prefix, const char_u *name) FUNC_ATTR_NONNULL_ALL { @@ -3153,10 +3180,8 @@ char_u *cat_prefix_varname(int prefix, const char_u *name) return varnamebuf; } -/* - * Function given to ExpandGeneric() to obtain the list of user defined - * (global/buffer/window/built-in) variable names. - */ +/// Function given to ExpandGeneric() to obtain the list of user defined +/// (global/buffer/window/built-in) variable names. char_u *get_user_var_name(expand_T *xp, int idx) { static size_t gdone; @@ -3188,10 +3213,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) } // b: variables - // In cmdwin, the alternative buffer should be used. - hashtab_T *ht = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? &prevwin->w_buffer->b_vars->dv_hashtab - : &curbuf->b_vars->dv_hashtab; + const hashtab_T *ht = &prevwin_curwin()->w_buffer->b_vars->dv_hashtab; if (bdone < ht->ht_used) { if (bdone++ == 0) { hi = ht->ht_array; @@ -3205,10 +3227,7 @@ char_u *get_user_var_name(expand_T *xp, int idx) } // w: variables - // In cmdwin, the alternative window should be used. - ht = (cmdwin_type != 0 && get_cmdline_type() == NUL) - ? &prevwin->w_vars->dv_hashtab - : &curwin->w_vars->dv_hashtab; + ht = &prevwin_curwin()->w_vars->dv_hashtab; if (wdone < ht->ht_used) { if (wdone++ == 0) { hi = ht->ht_array; @@ -3247,9 +3266,10 @@ char_u *get_user_var_name(expand_T *xp, int idx) // TODO(ZyX-I): move to eval/expressions -/// Return TRUE if "pat" matches "text". /// Does not use 'cpo' and always uses 'magic'. -static int pattern_match(char_u *pat, char_u *text, bool ic) +/// +/// @return TRUE if "pat" matches "text". +int pattern_match(char_u *pat, char_u *text, bool ic) { int matches = 0; regmatch_T regmatch; @@ -3269,10 +3289,10 @@ static int pattern_match(char_u *pat, char_u *text, bool ic) /// Handle a name followed by "(". Both for just "name(arg)" and for /// "expr->name(arg)". -// +/// /// @param arg Points to "(", will be advanced /// @param basetv "expr" for "expr->name(arg)" -// +/// /// @return OK or FAIL. static int eval_func(char_u **const arg, char_u *const name, const int name_len, typval_T *const rettv, const bool evaluate, typval_T *const basetv) @@ -3333,13 +3353,12 @@ static int eval_func(char_u **const arg, char_u *const name, const int name_len, * VAR_UNKNOWN. The function still returns FAIL for a syntax error. */ -/* - * Handle zero level expression. - * This calls eval1() and handles error message and nextcmd. - * Put the result in "rettv" when returning OK and "evaluate" is TRUE. - * Note: "rettv.v_lock" is not set. - * Return OK or FAIL. - */ +/// Handle zero level expression. +/// This calls eval1() and handles error message and nextcmd. +/// Put the result in "rettv" when returning OK and "evaluate" is TRUE. +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) { int ret; @@ -3372,17 +3391,15 @@ int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle top level expression: - * expr2 ? expr1 : expr1 - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Note: "rettv.v_lock" is not set. - * - * Return OK or FAIL. - */ +/// Handle top level expression: +/// expr2 ? expr1 : expr1 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// Note: "rettv.v_lock" is not set. +/// +/// @return OK or FAIL. int eval1(char_u **arg, typval_T *rettv, int evaluate) { int result; @@ -3448,15 +3465,13 @@ int eval1(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle first level expression: - * expr2 || expr2 || expr2 logical OR - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle first level expression: +/// expr2 || expr2 || expr2 logical OR +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval2(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3519,15 +3534,13 @@ static int eval2(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle second level expression: - * expr3 && expr3 && expr3 logical AND - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle second level expression: +/// expr3 && expr3 && expr3 logical AND +/// +/// @param arg must point to the first non-white of the expression. +/// `arg` is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval3(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3590,24 +3603,22 @@ static int eval3(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle third level expression: - * var1 == var2 - * var1 =~ var2 - * var1 != var2 - * var1 !~ var2 - * var1 > var2 - * var1 >= var2 - * var1 < var2 - * var1 <= var2 - * var1 is var2 - * var1 isnot var2 - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle third level expression: +/// var1 == var2 +/// var1 =~ var2 +/// var1 != var2 +/// var1 !~ var2 +/// var1 > var2 +/// var1 >= var2 +/// var1 < var2 +/// var1 <= var2 +/// var1 is var2 +/// var1 isnot var2 +/// +/// "arg" must point to the first non-white of the expression. +/// "arg" is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval4(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3701,18 +3712,16 @@ static int eval4(char_u **arg, typval_T *rettv, int evaluate) // TODO(ZyX-I): move to eval/expressions -/* - * Handle fourth level expression: - * + number addition - * - number subtraction - * . string concatenation - * .. string concatenation - * - * "arg" must point to the first non-white of the expression. - * "arg" is advanced to the next non-white after the recognized expression. - * - * Return OK or FAIL. - */ +/// Handle fourth level expression: +/// + number addition +/// - number subtraction +/// . string concatenation +/// .. string concatenation +/// +/// @param arg must point to the first non-white of the expression. +/// `arg` is advanced to the next non-white after the recognized expression. +/// +/// @return OK or FAIL. static int eval5(char_u **arg, typval_T *rettv, int evaluate) { typval_T var2; @@ -3965,18 +3974,11 @@ static int eval6(char_u **arg, typval_T *rettv, int evaluate, int want_string) f1 = f1 * f2; } else if (op == '/') { // Division by zero triggers error from AddressSanitizer - f1 = (f2 == 0 - ? ( + f1 = (f2 == 0 ? ( #ifdef NAN - f1 == 0 - ? NAN - : + f1 == 0 ? (float_T)NAN : #endif - (f1 > 0 - ? INFINITY - : -INFINITY) - ) - : f1 / f2); + (f1 > 0 ? (float_T)INFINITY : (float_T)-INFINITY)) : f1 / f2); } else { emsg(_("E804: Cannot use '%' with Float")); return FAIL; @@ -4258,7 +4260,8 @@ static int eval7(char_u **arg, typval_T *rettv, int evaluate, int want_string) /// Apply the leading "!" and "-" before an eval7 expression to "rettv". /// Adjusts "end_leaderp" until it is at "start_leader". -/// @return OK on success, FAIL on failure. +/// +/// @return OK on success, FAIL on failure. static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, const char_u **const end_leaderp) FUNC_ATTR_NONNULL_ALL @@ -4312,7 +4315,7 @@ static int eval7_leader(typval_T *const rettv, const char_u *const start_leader, /// @param lua_funcname If `rettv` refers to a v:lua function, this must point /// to the name of the Lua function to call (after the /// "v:lua." prefix). -/// @return OK on success, FAIL on failure. +/// @return OK on success, FAIL on failure. static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool evaluate, dict_T *const selfdict, typval_T *const basetv, const char_u *const lua_funcname) @@ -4360,9 +4363,13 @@ static int call_func_rettv(char_u **const arg, typval_T *const rettv, const bool } /// Evaluate "->method()". +/// /// @param verbose if true, give error messages. -/// @note "*arg" points to the '-'. -/// @return FAIL or OK. @note "*arg" is advanced to after the ')'. +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. +/// +/// @note "*arg" is advanced to after the ')'. static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool evaluate, const bool verbose) FUNC_ATTR_NONNULL_ALL @@ -4373,7 +4380,7 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool eva rettv->v_type = VAR_UNKNOWN; int ret = get_lambda_tv(arg, rettv, evaluate); - if (ret == NOTDONE) { + if (ret != OK) { return FAIL; } else if (**arg != '(') { if (verbose) { @@ -4399,8 +4406,10 @@ static int eval_lambda(char_u **const arg, typval_T *const rettv, const bool eva } /// Evaluate "->method()" or "->v:lua.method()". -/// @note "*arg" points to the '-'. -/// @return FAIL or OK. "*arg" is advanced to after the ')'. +/// +/// @param *arg points to the '-'. +/// +/// @return FAIL or OK. "*arg" is advanced to after the ')'. static int eval_method(char_u **const arg, typval_T *const rettv, const bool evaluate, const bool verbose) FUNC_ATTR_NONNULL_ALL @@ -4473,9 +4482,10 @@ static int eval_method(char_u **const arg, typval_T *const rettv, const bool eva /// Evaluate an "[expr]" or "[expr:expr]" index. Also "dict.key". /// "*arg" points to the '[' or '.'. -/// Returns FAIL or OK. "*arg" is advanced to after the ']'. /// /// @param verbose give error messages +/// +/// @returns FAIL or OK. "*arg" is advanced to after the ']'. static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) { bool empty1 = false; @@ -4523,7 +4533,7 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) * dict.name */ key = *arg + 1; - for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) { + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; len++) { } if (len == 0) { return FAIL; @@ -4780,19 +4790,18 @@ static int eval_index(char_u **arg, typval_T *rettv, int evaluate, int verbose) /// Get an option value /// -/// @param[in,out] arg Points to the '&' or '+' before the option name. Is +/// @param[in,out] arg Points to the '&' or '+' before the option name. Is /// advanced to the character after the option name. -/// @param[out] rettv Location where result is saved. -/// @param[in] evaluate If not true, rettv is not populated. +/// @param[out] rettv Location where result is saved. +/// @param[in] evaluate If not true, rettv is not populated. /// -/// @return OK or FAIL. +/// @return OK or FAIL. int get_option_tv(const char **const arg, typval_T *const rettv, const bool evaluate) FUNC_ATTR_NONNULL_ARG(1) { long numval; char_u *stringval; int opt_type; - int c; bool working = (**arg == '+'); // has("+option") int ret = OK; int opt_flags; @@ -4811,7 +4820,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval return OK; } - c = *option_end; + char c = *option_end; *option_end = NUL; opt_type = get_option_value(*arg, &numval, rettv == NULL ? NULL : &stringval, opt_flags); @@ -4828,7 +4837,7 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval } else if (opt_type == -1) { // hidden number option rettv->v_type = VAR_NUMBER; rettv->vval.v_number = 0; - } else if (opt_type == 1) { // number option + } else if (opt_type == 1 || opt_type == 2) { // number or boolean option rettv->v_type = VAR_NUMBER; rettv->vval.v_number = numval; } else { // string option @@ -4845,10 +4854,9 @@ int get_option_tv(const char **const arg, typval_T *const rettv, const bool eval return ret; } -/* - * Allocate a variable for a string constant. - * Return OK or FAIL. - */ +/// Allocate a variable for a string constant. +/// +/// @return OK or FAIL. static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *p; @@ -4985,10 +4993,9 @@ static int get_string_tv(char_u **arg, typval_T *rettv, int evaluate) return OK; } -/* - * Allocate a variable for a 'str''ing' constant. - * Return OK or FAIL. - */ +/// Allocate a variable for a 'str''ing' constant. +/// +/// @return OK or FAIL. static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *p; @@ -5041,7 +5048,7 @@ static int get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate) return OK; } -/// @return the function name of the partial. +/// @return the function name of the partial. char_u *partial_name(partial_T *pt) { if (pt->pt_name != NULL) { @@ -5080,7 +5087,8 @@ void partial_unref(partial_T *pt) } /// Allocate a variable for a List and fill it from "*arg". -/// Return OK or FAIL. +/// +/// @return OK or FAIL. static int get_list_tv(char_u **arg, typval_T *rettv, int evaluate) { list_T *l = NULL; @@ -5218,7 +5226,8 @@ int get_copyID(void) /// Do garbage collection for lists and dicts. /// /// @param testing true if called from test_garbagecollect_now(). -/// @returns true if some memory was freed. +/// +/// @return true if some memory was freed. bool garbage_collect(bool testing) { bool abort = false; @@ -5396,10 +5405,11 @@ bool garbage_collect(bool testing) /// Free lists and dictionaries that are no longer referenced. /// -/// @note This function may only be called from garbage_collect(). +/// @note This function may only be called from garbage_collect(). /// -/// @param copyID Free lists/dictionaries that don't have this ID. -/// @return true, if something was freed. +/// @param copyID Free lists/dictionaries that don't have this ID. +/// +/// @return true, if something was freed. static int free_unref_items(int copyID) { bool did_free = false; @@ -5634,7 +5644,7 @@ bool set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack /// Mark all lists and dicts referenced in given mark /// -/// @returns true if setting references failed somehow. +/// @return true if setting references failed somehow. static inline bool set_ref_in_fmark(fmark_T fm, int copyID) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5648,7 +5658,7 @@ static inline bool set_ref_in_fmark(fmark_T fm, int copyID) /// Mark all lists and dicts referenced in given list and the list itself /// -/// @returns true if setting references failed somehow. +/// @return true if setting references failed somehow. static inline bool set_ref_list(list_T *list, int copyID) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5664,7 +5674,7 @@ static inline bool set_ref_list(list_T *list, int copyID) /// Mark all lists and dicts referenced in given dict and the dict itself /// -/// @returns true if setting references failed somehow. +/// @return true if setting references failed somehow. static inline bool set_ref_dict(dict_T *dict, int copyID) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5679,8 +5689,9 @@ static inline bool set_ref_dict(dict_T *dict, int copyID) } -// Get the key for *{key: val} into "tv" and advance "arg". -// Return FAIL when there is no valid key. +/// Get the key for *{key: val} into "tv" and advance "arg". +/// +/// @return FAIL when there is no valid key. static int get_literal_key(char_u **arg, typval_T *tv) FUNC_ATTR_NONNULL_ALL { @@ -5698,9 +5709,10 @@ static int get_literal_key(char_u **arg, typval_T *tv) return OK; } -// Allocate a variable for a Dictionary and fill it from "*arg". -// "literal" is true for *{key: val} -// Return OK or FAIL. Returns NOTDONE for {expr}. +/// Allocate a variable for a Dictionary and fill it from "*arg". +/// "literal" is true for *{key: val} +/// +/// @return OK or FAIL. Returns NOTDONE for {expr}. static int dict_get_tv(char_u **arg, typval_T *rettv, int evaluate, bool literal) { dict_T *d = NULL; @@ -5810,10 +5822,10 @@ failret: /// This uses strtod(). setlocale(LC_NUMERIC, "C") has been used earlier to /// make sure this always uses a decimal point. /// -/// @param[in] text String to convert. -/// @param[out] ret_value Location where conversion result is saved. +/// @param[in] text String to convert. +/// @param[out] ret_value Location where conversion result is saved. /// -/// @return Length of the text that was consumed. +/// @return Length of the text that was consumed. size_t string2float(const char *const text, float_T *const ret_value) FUNC_ATTR_NONNULL_ALL { @@ -5821,15 +5833,15 @@ size_t string2float(const char *const text, float_T *const ret_value) // MS-Windows does not deal with "inf" and "nan" properly if (STRNICMP(text, "inf", 3) == 0) { - *ret_value = INFINITY; + *ret_value = (float_T)INFINITY; return 3; } if (STRNICMP(text, "-inf", 3) == 0) { - *ret_value = -INFINITY; + *ret_value = (float_T)-INFINITY; return 4; } if (STRNICMP(text, "nan", 3) == 0) { - *ret_value = NAN; + *ret_value = (float_T)NAN; return 3; } *ret_value = strtod(text, &s); @@ -5840,9 +5852,9 @@ size_t string2float(const char *const text, float_T *const ret_value) /// /// If the environment variable was not set, silently assume it is empty. /// -/// @param arg Points to the '$'. It is advanced to after the name. -/// @return FAIL if the name is invalid. +/// @param arg Points to the '$'. It is advanced to after the name. /// +/// @return FAIL if the name is invalid. static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *name; @@ -5891,145 +5903,7 @@ void get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv) } } -// Prepare "gap" for an assert error and add the sourcing position. -void prepare_assert_error(garray_T *gap) -{ - char buf[NUMBUFLEN]; - - ga_init(gap, 1, 100); - if (sourcing_name != NULL) { - ga_concat(gap, (char *)sourcing_name); - if (sourcing_lnum > 0) { - ga_concat(gap, " "); - } - } - if (sourcing_lnum > 0) { - vim_snprintf(buf, ARRAY_SIZE(buf), "line %" PRId64, (int64_t)sourcing_lnum); - ga_concat(gap, buf); - } - if (sourcing_name != NULL || sourcing_lnum > 0) { - ga_concat(gap, ": "); - } -} - -// Append "p[clen]" to "gap", escaping unprintable characters. -// Changes NL to \n, CR to \r, etc. -static void ga_concat_esc(garray_T *gap, const char_u *p, int clen) - FUNC_ATTR_NONNULL_ALL -{ - char_u buf[NUMBUFLEN]; - - if (clen > 1) { - memmove(buf, p, clen); - buf[clen] = NUL; - ga_concat(gap, (char *)buf); - } else { - switch (*p) { - case BS: - ga_concat(gap, "\\b"); break; - case ESC: - ga_concat(gap, "\\e"); break; - case FF: - ga_concat(gap, "\\f"); break; - case NL: - ga_concat(gap, "\\n"); break; - case TAB: - ga_concat(gap, "\\t"); break; - case CAR: - ga_concat(gap, "\\r"); break; - case '\\': - ga_concat(gap, "\\\\"); break; - default: - if (*p < ' ') { - vim_snprintf((char *)buf, NUMBUFLEN, "\\x%02x", *p); - ga_concat(gap, (char *)buf); - } else { - ga_append(gap, *p); - } - break; - } - } -} - -// Append "str" to "gap", escaping unprintable characters. -// Changes NL to \n, CR to \r, etc. -static void ga_concat_shorten_esc(garray_T *gap, const char_u *str) - FUNC_ATTR_NONNULL_ARG(1) -{ - char_u buf[NUMBUFLEN]; - - if (str == NULL) { - ga_concat(gap, "NULL"); - return; - } - - for (const char_u *p = str; *p != NUL; p++) { - int same_len = 1; - const char_u *s = p; - const int c = mb_ptr2char_adv(&s); - const int clen = s - p; - while (*s != NUL && c == utf_ptr2char(s)) { - same_len++; - s += clen; - } - if (same_len > 20) { - ga_concat(gap, "\\["); - ga_concat_esc(gap, p, clen); - ga_concat(gap, " occurs "); - vim_snprintf((char *)buf, NUMBUFLEN, "%d", same_len); - ga_concat(gap, (char *)buf); - ga_concat(gap, " times]"); - p = s - 1; - } else { - ga_concat_esc(gap, p, clen); - } - } -} - -// Fill "gap" with information about an assert error. -void fill_assert_error(garray_T *gap, typval_T *opt_msg_tv, char_u *exp_str, typval_T *exp_tv, - typval_T *got_tv, assert_type_T atype) -{ - char_u *tofree; - - if (opt_msg_tv->v_type != VAR_UNKNOWN) { - tofree = (char_u *)encode_tv2echo(opt_msg_tv, NULL); - ga_concat(gap, (char *)tofree); - xfree(tofree); - ga_concat(gap, ": "); - } - - if (atype == ASSERT_MATCH || atype == ASSERT_NOTMATCH) { - ga_concat(gap, "Pattern "); - } else if (atype == ASSERT_NOTEQUAL) { - ga_concat(gap, "Expected not equal to "); - } else { - ga_concat(gap, "Expected "); - } - - if (exp_str == NULL) { - tofree = (char_u *)encode_tv2string(exp_tv, NULL); - ga_concat_shorten_esc(gap, tofree); - xfree(tofree); - } else { - ga_concat_shorten_esc(gap, exp_str); - } - - if (atype != ASSERT_NOTEQUAL) { - if (atype == ASSERT_MATCH) { - ga_concat(gap, " does not match "); - } else if (atype == ASSERT_NOTMATCH) { - ga_concat(gap, " does match "); - } else { - ga_concat(gap, " but got "); - } - tofree = (char_u *)encode_tv2string(got_tv, NULL); - ga_concat_shorten_esc(gap, tofree); - xfree(tofree); - } -} - -// Add an assert error to v:errors. +/// Add an assert error to v:errors. void assert_error(garray_T *gap) { struct vimvar *vp = &vimvars[VV_ERRORS]; @@ -6042,328 +5916,6 @@ void assert_error(garray_T *gap) (const char *)gap->ga_data, (ptrdiff_t)gap->ga_len); } -int assert_equal_common(typval_T *argvars, assert_type_T atype) - FUNC_ATTR_NONNULL_ALL -{ - garray_T ga; - - if (tv_equal(&argvars[0], &argvars[1], false, false) - != (atype == ASSERT_EQUAL)) { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, - &argvars[0], &argvars[1], atype); - assert_error(&ga); - ga_clear(&ga); - return 1; - } - return 0; -} - -int assert_equalfile(typval_T *argvars) - FUNC_ATTR_NONNULL_ALL -{ - char buf1[NUMBUFLEN]; - char buf2[NUMBUFLEN]; - const char *const fname1 = tv_get_string_buf_chk(&argvars[0], buf1); - const char *const fname2 = tv_get_string_buf_chk(&argvars[1], buf2); - garray_T ga; - - if (fname1 == NULL || fname2 == NULL) { - return 0; - } - - IObuff[0] = NUL; - FILE *const fd1 = os_fopen(fname1, READBIN); - char line1[200]; - char line2[200]; - ptrdiff_t lineidx = 0; - if (fd1 == NULL) { - snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname1); - } else { - FILE *const fd2 = os_fopen(fname2, READBIN); - if (fd2 == NULL) { - fclose(fd1); - snprintf((char *)IObuff, IOSIZE, (char *)e_notread, fname2); - } else { - int64_t linecount = 1; - for (int64_t count = 0;; count++) { - const int c1 = fgetc(fd1); - const int c2 = fgetc(fd2); - if (c1 == EOF) { - if (c2 != EOF) { - STRCPY(IObuff, "first file is shorter"); - } - break; - } else if (c2 == EOF) { - STRCPY(IObuff, "second file is shorter"); - break; - } else { - line1[lineidx] = c1; - line2[lineidx] = c2; - lineidx++; - if (c1 != c2) { - snprintf((char *)IObuff, IOSIZE, - "difference at byte %" PRId64 ", line %" PRId64, - count, linecount); - break; - } - } - if (c1 == NL) { - linecount++; - lineidx = 0; - } else if (lineidx + 2 == (ptrdiff_t)sizeof(line1)) { - memmove(line1, line1 + 100, lineidx - 100); - memmove(line2, line2 + 100, lineidx - 100); - lineidx -= 100; - } - } - fclose(fd1); - fclose(fd2); - } - } - if (IObuff[0] != NUL) { - prepare_assert_error(&ga); - if (argvars[2].v_type != VAR_UNKNOWN) { - char *const tofree = encode_tv2echo(&argvars[2], NULL); - ga_concat(&ga, tofree); - xfree(tofree); - ga_concat(&ga, ": "); - } - ga_concat(&ga, (char *)IObuff); - if (lineidx > 0) { - line1[lineidx] = NUL; - line2[lineidx] = NUL; - ga_concat(&ga, " after \""); - ga_concat(&ga, line1); - if (STRCMP(line1, line2) != 0) { - ga_concat(&ga, "\" vs \""); - ga_concat(&ga, line2); - } - ga_concat(&ga, "\""); - } - assert_error(&ga); - ga_clear(&ga); - return 1; - } - return 0; -} - -int assert_inrange(typval_T *argvars) - FUNC_ATTR_NONNULL_ALL -{ - bool error = false; - - if (argvars[0].v_type == VAR_FLOAT - || argvars[1].v_type == VAR_FLOAT - || argvars[2].v_type == VAR_FLOAT) { - const float_T flower = tv_get_float(&argvars[0]); - const float_T fupper = tv_get_float(&argvars[1]); - const float_T factual = tv_get_float(&argvars[2]); - - if (factual < flower || factual > fupper) { - garray_T ga; - prepare_assert_error(&ga); - if (argvars[3].v_type != VAR_UNKNOWN) { - char_u *const tofree = (char_u *)encode_tv2string(&argvars[3], NULL); - ga_concat(&ga, (char *)tofree); - xfree(tofree); - } else { - char msg[80]; - vim_snprintf(msg, sizeof(msg), "Expected range %g - %g, but got %g", - flower, fupper, factual); - ga_concat(&ga, msg); - } - assert_error(&ga); - ga_clear(&ga); - return 1; - } - } else { - const varnumber_T lower = tv_get_number_chk(&argvars[0], &error); - const varnumber_T upper = tv_get_number_chk(&argvars[1], &error); - const varnumber_T actual = tv_get_number_chk(&argvars[2], &error); - - if (error) { - return 0; - } - if (actual < lower || actual > upper) { - garray_T ga; - prepare_assert_error(&ga); - - char msg[55]; - vim_snprintf(msg, sizeof(msg), - "range %" PRIdVARNUMBER " - %" PRIdVARNUMBER ",", - lower, upper); // -V576 - fill_assert_error(&ga, &argvars[3], (char_u *)msg, NULL, &argvars[2], - ASSERT_INRANGE); - assert_error(&ga); - ga_clear(&ga); - return 1; - } - } - return 0; -} - -// Common for assert_true() and assert_false(). -int assert_bool(typval_T *argvars, bool is_true) - FUNC_ATTR_NONNULL_ALL -{ - bool error = false; - garray_T ga; - - if ((argvars[0].v_type != VAR_NUMBER - || (tv_get_number_chk(&argvars[0], &error) == 0) == is_true - || error) - && (argvars[0].v_type != VAR_BOOL - || (argvars[0].vval.v_bool - != (BoolVarValue)(is_true - ? kBoolVarTrue - : kBoolVarFalse)))) { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[1], - (char_u *)(is_true ? "True" : "False"), - NULL, &argvars[0], ASSERT_OTHER); - assert_error(&ga); - ga_clear(&ga); - return 1; - } - return 0; -} - -int assert_exception(typval_T *argvars) - FUNC_ATTR_NONNULL_ALL -{ - garray_T ga; - - const char *const error = tv_get_string_chk(&argvars[0]); - if (vimvars[VV_EXCEPTION].vv_str == NULL) { - prepare_assert_error(&ga); - ga_concat(&ga, "v:exception is not set"); - assert_error(&ga); - ga_clear(&ga); - return 1; - } else if (error != NULL - && strstr((char *)vimvars[VV_EXCEPTION].vv_str, error) == NULL) { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[1], NULL, &argvars[0], - &vimvars[VV_EXCEPTION].vv_tv, ASSERT_OTHER); - assert_error(&ga); - ga_clear(&ga); - return 1; - } - return 0; -} - -static void assert_append_cmd_or_arg(garray_T *gap, typval_T *argvars, const char *cmd) - FUNC_ATTR_NONNULL_ALL -{ - if (argvars[1].v_type != VAR_UNKNOWN && argvars[2].v_type != VAR_UNKNOWN) { - char *const tofree = encode_tv2echo(&argvars[2], NULL); - ga_concat(gap, tofree); - xfree(tofree); - } else { - ga_concat(gap, cmd); - } -} - -int assert_beeps(typval_T *argvars, bool no_beep) - FUNC_ATTR_NONNULL_ALL -{ - const char *const cmd = tv_get_string_chk(&argvars[0]); - int ret = 0; - - called_vim_beep = false; - suppress_errthrow = true; - emsg_silent = false; - do_cmdline_cmd(cmd); - if (no_beep ? called_vim_beep : !called_vim_beep) { - garray_T ga; - prepare_assert_error(&ga); - if (no_beep) { - ga_concat(&ga, "command did beep: "); - } else { - ga_concat(&ga, "command did not beep: "); - } - ga_concat(&ga, cmd); - assert_error(&ga); - ga_clear(&ga); - ret = 1; - } - - suppress_errthrow = false; - emsg_on_display = false; - return ret; -} - -int assert_fails(typval_T *argvars) - FUNC_ATTR_NONNULL_ALL -{ - const char *const cmd = tv_get_string_chk(&argvars[0]); - garray_T ga; - int ret = 0; - int save_trylevel = trylevel; - - // trylevel must be zero for a ":throw" command to be considered failed - trylevel = 0; - called_emsg = false; - suppress_errthrow = true; - emsg_silent = true; - - do_cmdline_cmd(cmd); - if (!called_emsg) { - prepare_assert_error(&ga); - ga_concat(&ga, "command did not fail: "); - assert_append_cmd_or_arg(&ga, argvars, cmd); - assert_error(&ga); - ga_clear(&ga); - ret = 1; - } else if (argvars[1].v_type != VAR_UNKNOWN) { - char buf[NUMBUFLEN]; - const char *const error = tv_get_string_buf_chk(&argvars[1], buf); - - if (error == NULL - || strstr((char *)vimvars[VV_ERRMSG].vv_str, error) == NULL) { - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, &argvars[1], - &vimvars[VV_ERRMSG].vv_tv, ASSERT_OTHER); - ga_concat(&ga, ": "); - assert_append_cmd_or_arg(&ga, argvars, cmd); - assert_error(&ga); - ga_clear(&ga); - ret = 1; - } - } - - trylevel = save_trylevel; - called_emsg = false; - suppress_errthrow = false; - emsg_silent = false; - emsg_on_display = false; - set_vim_var_string(VV_ERRMSG, NULL, 0); - return ret; -} - -int assert_match_common(typval_T *argvars, assert_type_T atype) - FUNC_ATTR_NONNULL_ALL -{ - char buf1[NUMBUFLEN]; - char buf2[NUMBUFLEN]; - const char *const pat = tv_get_string_buf_chk(&argvars[0], buf1); - const char *const text = tv_get_string_buf_chk(&argvars[1], buf2); - - if (pat == NULL || text == NULL) { - emsg(_(e_invarg)); - } else if (pattern_match((char_u *)pat, (char_u *)text, false) - != (atype == ASSERT_MATCH)) { - garray_T ga; - prepare_assert_error(&ga); - fill_assert_error(&ga, &argvars[2], NULL, &argvars[0], &argvars[1], atype); - assert_error(&ga); - ga_clear(&ga); - return 1; - } - return 0; -} - /// 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) @@ -6371,15 +5923,13 @@ 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 win_id2wp(tv_get_number(vp)); } return find_win_by_nr(vp, NULL); } -/* - * Implementation of map() and filter(). - */ +/// Implementation of map() and filter(). void filter_map(typval_T *argvars, typval_T *rettv, int map) { typval_T *expr; @@ -6439,6 +5989,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) if (argvars[0].v_type == VAR_DICT) { vimvars[VV_KEY].vv_type = VAR_STRING; + const VarLockStatus prev_lock = d->dv_lock; + if (map && d->dv_lock == VAR_UNLOCKED) { + d->dv_lock = VAR_LOCKED; + } ht = &d->dv_hashtab; hash_lock(ht); todo = (int)ht->ht_used; @@ -6469,6 +6023,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } } hash_unlock(ht); + d->dv_lock = prev_lock; } else if (argvars[0].v_type == VAR_BLOB) { vimvars[VV_KEY].vv_type = VAR_NUMBER; @@ -6501,6 +6056,10 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) assert(argvars[0].v_type == VAR_LIST); vimvars[VV_KEY].vv_type = VAR_NUMBER; + const VarLockStatus prev_lock = tv_list_locked(l); + if (map && tv_list_locked(l) == VAR_UNLOCKED) { + tv_list_set_lock(l, VAR_LOCKED); + } for (listitem_T *li = tv_list_first(l); li != NULL;) { if (map && var_check_lock(TV_LIST_ITEM_TV(li)->v_lock, arg_errmsg, @@ -6519,6 +6078,7 @@ void filter_map(typval_T *argvars, typval_T *rettv, int map) } idx++; } + tv_list_set_lock(l, prev_lock); } restore_vimvar(VV_KEY, &save_key); @@ -6603,8 +6163,9 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref, FunPtr : (const char *)s)); // Don't check an autoload name for existence here. } else if (trans_name != NULL - && (is_funcref ? find_func(trans_name) == NULL - : !translated_function_exists((const char *)trans_name))) { + && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists((const char *)trans_name))) { semsg(_("E700: Unknown function: %s"), s); } else { int dict_idx = 0; @@ -6730,7 +6291,7 @@ theend: xfree(trans_name); } -/// Returns buffer options, variables and other attributes in a dictionary. +/// @return buffer options, variables and other attributes in a dictionary. dict_T *get_buffer_info(buf_T *buf) { dict_T *const dict = tv_dict_alloc(); @@ -6774,12 +6335,12 @@ dict_T *get_buffer_info(buf_T *buf) /// /// @note Unlike tv_get_lnum(), this one supports only "$" special string. /// -/// @param[in] tv Object to get value from. Is expected to be a number or +/// @param[in] tv Object to get value from. Is expected to be a number or /// a special string "$". -/// @param[in] buf Buffer to take last line number from in case tv is "$". May -/// be NULL, in this case "$" results in zero return. +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. /// -/// @return Line number or 0 in case of error. +/// @return Line number or 0 in case of error. linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT { @@ -6815,8 +6376,8 @@ void get_qf_loc_list(int is_qf, win_T *wp, typval_T *what_arg, typval_T *rettv) } } -/// Returns information (variables, options, etc.) about a tab page -/// as a dictionary. +/// @return information (variables, options, etc.) about a tab page +/// as a dictionary. dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) { dict_T *const dict = tv_dict_alloc(); @@ -6835,7 +6396,7 @@ dict_T *get_tabpage_info(tabpage_T *tp, int tp_idx) return dict; } -/// Returns information about a window as a dictionary. +/// @return information about a window as a dictionary. dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) { dict_T *const dict = tv_dict_alloc(); @@ -6851,7 +6412,7 @@ dict_T *get_win_info(win_T *wp, int16_t tpnr, int16_t winnr) tv_dict_add_nr(dict, S_LEN("width"), wp->w_width); tv_dict_add_nr(dict, S_LEN("bufnr"), wp->w_buffer->b_fnum); tv_dict_add_nr(dict, S_LEN("wincol"), wp->w_wincol + 1); - + tv_dict_add_nr(dict, S_LEN("textoff"), win_col_off(wp)); tv_dict_add_nr(dict, S_LEN("terminal"), bt_terminal(wp->w_buffer)); tv_dict_add_nr(dict, S_LEN("quickfix"), bt_quickfix(wp->w_buffer)); tv_dict_add_nr(dict, S_LEN("loclist"), @@ -6926,10 +6487,9 @@ win_T *find_tabwin(typval_T *wvp, typval_T *tvp) /// @param off 1 for gettabwinvar() void getwinvar(typval_T *argvars, typval_T *rettv, int off) { - win_T *win, *oldcurwin; + win_T *win; dictitem_T *v; tabpage_T *tp = NULL; - tabpage_T *oldtabpage = NULL; bool done = false; if (off == 1) { @@ -6949,8 +6509,8 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) // otherwise the window is not valid. Only do this when needed, // autocommands get blocked. bool need_switch_win = tp != curtab || win != curwin; - if (!need_switch_win - || switch_win(&oldcurwin, &oldtabpage, win, tp, true) == OK) { + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&') { if (varname[1] == NUL) { // get all window-local options in a dict @@ -6978,7 +6538,7 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) if (need_switch_win) { // restore previous notion of curwin - restore_win(oldcurwin, oldtabpage, true); + restore_win(&switchwin, true); } } emsg_off--; @@ -6989,12 +6549,10 @@ void getwinvar(typval_T *argvars, typval_T *rettv, int off) } } -/* - * 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 - * prompt. The third argument to f_inputdialog() specifies the value to return - * when the user cancels the prompt. - */ +/// 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 +/// prompt. The third argument to f_inputdialog() specifies the value to return +/// when the user cancels the prompt. void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, const bool secret) FUNC_ATTR_NONNULL_ALL @@ -7126,10 +6684,10 @@ void get_user_input(const typval_T *const argvars, typval_T *const rettv, const /// Turn a dictionary into a list /// -/// @param[in] tv Dictionary to convert. Is checked for actually being -/// a dictionary, will give an error if not. -/// @param[out] rettv Location where result will be saved. -/// @param[in] what What to save in rettv. +/// @param[in] tv Dictionary to convert. Is checked for actually being +/// a dictionary, will give an error if not. +/// @param[out] rettv Location where result will be saved. +/// @param[in] what What to save in rettv. void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType what) { if (tv->v_type != VAR_DICT) { @@ -7146,30 +6704,30 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha typval_T tv_item = { .v_lock = VAR_UNLOCKED }; switch (what) { - case kDictListKeys: - tv_item.v_type = VAR_STRING; - tv_item.vval.v_string = vim_strsave(di->di_key); - break; - case kDictListValues: - tv_copy(&di->di_tv, &tv_item); - break; - case kDictListItems: { - // items() - list_T *const sub_l = tv_list_alloc(2); - tv_item.v_type = VAR_LIST; - tv_item.vval.v_list = sub_l; - tv_list_ref(sub_l); - - tv_list_append_owned_tv(sub_l, (typval_T) { + case kDictListKeys: + tv_item.v_type = VAR_STRING; + tv_item.vval.v_string = vim_strsave(di->di_key); + break; + case kDictListValues: + tv_copy(&di->di_tv, &tv_item); + break; + case kDictListItems: { + // items() + list_T *const sub_l = tv_list_alloc(2); + tv_item.v_type = VAR_LIST; + tv_item.vval.v_list = sub_l; + tv_list_ref(sub_l); + + tv_list_append_owned_tv(sub_l, (typval_T) { .v_type = VAR_STRING, .v_lock = VAR_UNLOCKED, .vval.v_string = (char_u *)xstrdup((const char *)di->di_key), }); - tv_list_append_tv(sub_l, &di->di_tv); + tv_list_append_tv(sub_l, &di->di_tv); - break; - } + break; + } } tv_list_append_owned_tv(rettv->vval.v_list, tv_item); @@ -7182,7 +6740,7 @@ void dict_list(typval_T *const tv, typval_T *const rettv, const DictListType wha /// @param[out] cmd Returns the command or executable name. /// @param[out] executable Returns `false` if argv[0] is not executable. /// -/// @returns Result of `shell_build_argv()` if `cmd_tv` is a String. +/// @return Result of `shell_build_argv()` if `cmd_tv` is a String. /// Else, string values of `cmd_tv` copied to a (char **) list with /// argv[0] resolved to full path ($PATHEXT-resolved on Windows). char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) @@ -7246,10 +6804,10 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) /// Fill a dictionary with all applicable maparg() like dictionaries /// -/// @param dict The dictionary to be filled -/// @param mp The maphash that contains the mapping information -/// @param buffer_value The "buffer" value -/// @param compatible True for compatible with old maparg() dict +/// @param dict The dictionary to be filled +/// @param mp The maphash that contains the mapping information +/// @param buffer_value The "buffer" value +/// @param compatible True for compatible with old maparg() dict void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buffer_value, bool compatible) FUNC_ATTR_NONNULL_ALL @@ -7269,12 +6827,19 @@ void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buf noremap_value = mp->m_noremap == REMAP_SCRIPT ? 2 : !!mp->m_noremap; } - if (compatible) { - tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + if (mp->m_luaref != LUA_NOREF) { + tv_dict_add_nr(dict, S_LEN("callback"), mp->m_luaref); } else { - tv_dict_add_allocated_str(dict, S_LEN("rhs"), - str2special_save((const char *)mp->m_str, false, - true)); + if (compatible) { + tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); + } else { + tv_dict_add_allocated_str(dict, S_LEN("rhs"), + str2special_save((const char *)mp->m_str, false, + true)); + } + } + if (mp->m_desc != NULL) { + tv_dict_add_allocated_str(dict, S_LEN("desc"), xstrdup(mp->m_desc)); } tv_dict_add_allocated_str(dict, S_LEN("lhs"), lhs); tv_dict_add_nr(dict, S_LEN("noremap"), noremap_value); @@ -7288,30 +6853,6 @@ void mapblock_fill_dict(dict_T *const dict, const mapblock_T *const mp, long buf tv_dict_add_allocated_str(dict, S_LEN("mode"), mapmode); } -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(_(e_invalwindow)); - return FAIL; - } - } - - return OK; -} - void return_register(int regname, typval_T *rettv) { char_u buf[2] = { regname, 0 }; @@ -7473,11 +7014,9 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off) typval_T *varp = &argvars[off + 2]; if (win != NULL && varname != NULL && varp != NULL) { - win_T *save_curwin; - tabpage_T *save_curtab; bool need_switch_win = tp != curtab || win != curwin; - if (!need_switch_win - || switch_win(&save_curwin, &save_curtab, win, tp, true) == OK) { + switchwin_T switchwin; + if (!need_switch_win || switch_win(&switchwin, win, tp, true) == OK) { if (*varname == '&') { long numval; bool error = false; @@ -7499,7 +7038,7 @@ void setwinvar(typval_T *argvars, typval_T *rettv, int off) } } if (need_switch_win) { - restore_win(save_curwin, save_curtab, true); + restore_win(&switchwin, true); } } } @@ -7541,7 +7080,7 @@ static list_T *string_to_list(const char *str, size_t len, const bool keepempty) return list; } -// os_system wrapper. Handles 'verbose', :profile, and v:shell_error. +/// os_system wrapper. Handles 'verbose', :profile, and v:shell_error. void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist) { proftime_T wait_time; @@ -7666,6 +7205,7 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) callback->type = kCallbackFuncref; } } else if (nlua_is_table_from_lua(arg)) { + // TODO(tjdvries): UnifiedCallback char_u *name = nlua_register_table_as_callable(arg); if (name != NULL) { @@ -7695,6 +7235,8 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co { partial_T *partial; char_u *name; + Array args = ARRAY_DICT_INIT; + Object rv; switch (callback->type) { case kCallbackFuncref: name = callback->data.funcref; @@ -7706,6 +7248,15 @@ bool callback_call(Callback *const callback, const int argcount_in, typval_T *co name = partial_name(partial); break; + case kCallbackLua: + rv = nlua_call_ref(callback->data.luaref, NULL, args, true, NULL); + switch (rv.type) { + case kObjectTypeBoolean: + return rv.data.boolean; + default: + return false; + } + case kCallbackNone: return false; break; @@ -7798,7 +7349,7 @@ void add_timer_info_all(typval_T *rettv) }) } -// invoked on the main loop +/// invoked on the main loop void timer_due_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; @@ -7884,8 +7435,8 @@ void timer_stop(timer_T *timer) time_watcher_close(&timer->tw, timer_close_cb); } -// This will be run on the main loop after the last timer_due_cb, so at this -// point it is safe to free the callback. +/// This will be run on the main loop after the last timer_due_cb, so at this +/// point it is safe to free the callback. static void timer_close_cb(TimeWatcher *tw, void *data) { timer_T *timer = (timer_T *)data; @@ -8110,6 +7661,68 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) return ret; } +/// Convert the specified byte index of line 'lnum' in buffer 'buf' to a +/// character index. Works only for loaded buffers. Returns -1 on failure. +/// The index of the first byte and the first character is zero. +int buf_byteidx_to_charidx(buf_T *buf, int lnum, int byteidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char_u *str = ml_get_buf(buf, lnum, false); + + if (*str == NUL) { + return 0; + } + + // count the number of characters + char_u *t = str; + int count; + for (count = 0; *t != NUL && t <= str + byteidx; count++) { + t += utfc_ptr2len(t); + } + + // In insert mode, when the cursor is at the end of a non-empty line, + // byteidx points to the NUL character immediately past the end of the + // string. In this case, add one to the character count. + if (*t == NUL && byteidx != 0 && t == str + byteidx) { + count++; + } + + return count - 1; +} + +/// Convert the specified character index of line 'lnum' in buffer 'buf' to a +/// byte index. Works only for loaded buffers. +/// The index of the first byte and the first character is zero. +/// +/// @return -1 on failure. +int buf_charidx_to_byteidx(buf_T *buf, int lnum, int charidx) +{ + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return -1; + } + + if (lnum > buf->b_ml.ml_line_count) { + lnum = buf->b_ml.ml_line_count; + } + + char_u *str = ml_get_buf(buf, lnum, false); + + // Convert the character offset to a byte offset + char_u *t = str; + while (*t != NUL && --charidx > 0) { + t += utfc_ptr2len(t); + } + + return t - str; +} + /// Translate a VimL object into a position /// /// Accepts VAR_LIST and VAR_STRING objects. Does not give an error for invalid @@ -8118,13 +7731,14 @@ char *save_tv_as_string(typval_T *tv, ptrdiff_t *const len, bool endnl) /// @param[in] tv Object to translate. /// @param[in] dollar_lnum True when "$" is last line. /// @param[out] ret_fnum Set to fnum for marks. +/// @param[in] charcol True to return character column. /// /// @return Pointer to position or NULL in case of error (e.g. invalid type). -pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum) +pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret_fnum, + const bool charcol) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { static pos_T pos; - pos_T *pp; // Argument can be [lnum, col, coladd]. if (tv->v_type == VAR_LIST) { @@ -8150,7 +7764,11 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (error) { return NULL; } - len = (long)STRLEN(ml_get(pos.lnum)); + if (charcol) { + len = mb_charlen(ml_get(pos.lnum)); + } else { + len = STRLEN(ml_get(pos.lnum)); + } // We accept "$" for the column number: last column. li = tv_list_find(l, 1L); @@ -8180,21 +7798,31 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret if (name == NULL) { return NULL; } - if (name[0] == '.') { // Cursor. - return &curwin->w_cursor; - } - if (name[0] == 'v' && name[1] == NUL) { // Visual start. + + pos.lnum = 0; + if (name[0] == '.') { + // cursor + pos = curwin->w_cursor; + } else if (name[0] == 'v' && name[1] == NUL) { + // Visual start if (VIsual_active) { - return &VIsual; + pos = VIsual; + } else { + pos = curwin->w_cursor; } - return &curwin->w_cursor; - } - if (name[0] == '\'') { // Mark. - pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); + } else if (name[0] == '\'') { + // mark + const pos_T *const pp = getmark_buf_fnum(curbuf, (uint8_t)name[1], false, ret_fnum); if (pp == NULL || pp == (pos_T *)-1 || pp->lnum <= 0) { return NULL; } - return pp; + pos = *pp; + } + if (pos.lnum != 0) { + if (charcol) { + pos.col = buf_byteidx_to_charidx(curbuf, pos.lnum, pos.col); + } + return &pos; } pos.coladd = 0; @@ -8219,22 +7847,25 @@ pos_T *var2fpos(const typval_T *const tv, const bool dollar_lnum, int *const ret pos.col = 0; } else { pos.lnum = curwin->w_cursor.lnum; - pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + if (charcol) { + pos.col = (colnr_T)mb_charlen(get_cursor_line_ptr()); + } else { + pos.col = (colnr_T)STRLEN(get_cursor_line_ptr()); + } } return &pos; } return NULL; } -/* - * Convert list in "arg" into a position and optional file number. - * When "fnump" is NULL there is no file number, only 3 items. - * Note that the column is passed on as-is, the caller may want to decrement - * it to use 1 for the first column. - * Return FAIL when conversion is not possible, doesn't check the position for - * validity. - */ -int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) +/// Convert list in "arg" into a position and optional file number. +/// When "fnump" is NULL there is no file number, only 3 items. +/// Note that the column is passed on as-is, the caller may want to decrement +/// it to use 1 for the first column. +/// +/// @return FAIL when conversion is not possible, doesn't check the position for +/// validity. +int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp, bool charcol) { list_T *l; long i = 0; @@ -8270,6 +7901,15 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) if (n < 0) { return FAIL; } + // If character position is specified, then convert to byte position + if (charcol) { + // Get the text for the specified line in a loaded buffer + buf_T *buf = buflist_findnr(fnump == NULL ? curbuf->b_fnum : *fnump); + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + return FAIL; + } + n = buf_charidx_to_byteidx(buf, posp->lnum, n) + 1; + } posp->col = n; n = tv_list_find_nr(l, i, NULL); // off @@ -8286,11 +7926,10 @@ int list2fpos(typval_T *arg, pos_T *posp, int *fnump, colnr_T *curswantp) return OK; } -/* - * Get the length of an environment variable name. - * Advance "arg" to the first character after the name. - * Return 0 for error. - */ +/// Get the length of an environment variable name. +/// Advance "arg" to the first character after the name. +/// +/// @return 0 for error. static int get_env_len(const char_u **arg) { int len; @@ -8307,9 +7946,11 @@ static int get_env_len(const char_u **arg) return len; } -// Get the length of the name of a function or internal variable. -// "arg" is advanced to the first non-white character after the name. -// Return 0 if something is wrong. +/// Get the length of the name of a function or internal variable. +/// +/// @param arg is advanced to the first non-white character after the name. +/// +/// @return 0 if something is wrong. int get_id_len(const char **const arg) { int len; @@ -8337,15 +7978,15 @@ int get_id_len(const char **const arg) return len; } -/* - * Get the length of the name of a variable or function. - * Only the name is recognized, does not handle ".key" or "[idx]". - * "arg" is advanced to the first non-white character after the name. - * Return -1 if curly braces expansion failed. - * Return 0 if something else is wrong. - * If the name contains 'magic' {}'s, expand them and return the - * expanded name in an allocated string via 'alias' - caller must free. - */ +/// Get the length of the name of a variable or function. +/// Only the name is recognized, does not handle ".key" or "[idx]". +/// +/// @param arg is advanced to the first non-white character after the name. +/// If the name contains 'magic' {}'s, expand them and return the +/// expanded name in an allocated string via 'alias' - caller must free. +/// +/// @return -1 if curly braces expansion failed or +/// 0 if something else is wrong. int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbose) { int len; @@ -8402,12 +8043,15 @@ int get_name_len(const char **const arg, char **alias, bool evaluate, bool verbo return len; } -// Find the end of a variable or function name, taking care of magic braces. -// If "expr_start" is not NULL then "expr_start" and "expr_end" are set to the -// start and end of the first magic braces item. -// "flags" can have FNE_INCL_BR and FNE_CHECK_START. -// Return a pointer to just after the name. Equal to "arg" if there is no -// valid name. +/// Find the end of a variable or function name, taking care of magic braces. +/// +/// @param expr_start if not NULL, then `expr_start` and `expr_end` are set to the +/// start and end of the first magic braces item. +/// +/// @param flags can have FNE_INCL_BR and FNE_CHECK_START. +/// +/// @return a pointer to just after the name. Equal to "arg" if there is no +/// valid name. const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const char_u **expr_end, int flags) { @@ -8485,19 +8129,17 @@ const char_u *find_name_end(const char_u *arg, const char_u **expr_start, const return p; } -/* - * Expands out the 'magic' {}'s in a variable/function name. - * Note that this can call itself recursively, to deal with - * constructs like foo{bar}{baz}{bam} - * The four pointer arguments point to "foo{expre}ss{ion}bar" - * "in_start" ^ - * "expr_start" ^ - * "expr_end" ^ - * "in_end" ^ - * - * Returns a new allocated string, which the caller must free. - * Returns NULL for failure. - */ +/// Expands out the 'magic' {}'s in a variable/function name. +/// Note that this can call itself recursively, to deal with +/// constructs like foo{bar}{baz}{bam} +/// The four pointer arguments point to "foo{expre}ss{ion}bar" +/// "in_start" ^ +/// "expr_start" ^ +/// "expr_end" ^ +/// "in_end" ^ +/// +/// @return a new allocated string, which the caller must free or +/// NULL for failure. static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, char_u *expr_end, char_u *in_end) { @@ -8544,44 +8186,42 @@ static char_u *make_expanded_name(const char_u *in_start, char_u *expr_start, ch return retval; } -/* - * Return TRUE if character "c" can be used in a variable or function name. - * Does not include '{' or '}' for magic braces. - */ +/// @return TRUE if character "c" can be used in a variable or function name. +/// Does not include '{' or '}' for magic braces. int eval_isnamec(int c) { return ASCII_ISALNUM(c) || c == '_' || c == ':' || c == AUTOLOAD_CHAR; } -/* - * Return TRUE if character "c" can be used as the first character in a - * variable or function name (excluding '{' and '}'). - */ +/// @return TRUE if character "c" can be used as the first character in a +/// variable or function name (excluding '{' and '}'). int eval_isnamec1(int c) { return ASCII_ISALPHA(c) || c == '_'; } -/* - * Get number v: variable value. - */ +/// Get typval_T v: variable value. +typval_T *get_vim_var_tv(int idx) +{ + return &vimvars[idx].vv_tv; +} + +/// Get number v: variable value. varnumber_T get_vim_var_nr(int idx) FUNC_ATTR_PURE { return vimvars[idx].vv_nr; } -// Get string v: variable value. Uses a static buffer, can only be used once. -// If the String variable has never been set, return an empty string. -// Never returns NULL; +/// Get string v: variable value. Uses a static buffer, can only be used once. +/// If the String variable has never been set, return an empty string. +/// Never returns NULL; char_u *get_vim_var_str(int idx) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_RET { return (char_u *)tv_get_string(&vimvars[idx].vv_tv); } -/* - * Get List v: variable value. Caller must take care of reference count when - * needed. - */ +/// Get List v: variable value. Caller must take care of reference count when +/// needed. list_T *get_vim_var_list(int idx) FUNC_ATTR_PURE { return vimvars[idx].vv_list; @@ -8594,9 +8234,7 @@ dict_T *get_vim_var_dict(int idx) FUNC_ATTR_PURE return vimvars[idx].vv_dict; } -/* - * Set v:char to character "c". - */ +/// Set v:char to character "c". void set_vim_var_char(int c) { char buf[MB_MAXBYTES + 1]; @@ -8605,10 +8243,9 @@ void set_vim_var_char(int c) set_vim_var_string(VV_CHAR, buf, -1); } -/* - * Set v:count to "count" and v:count1 to "count1". - * When "set_prevcount" is TRUE first set v:prevcount from v:count. - */ +/// Set v:count to "count" and v:count1 to "count1". +/// +/// @param set_prevcount if TRUE, first set v:prevcount from v:count. void set_vcount(long count, long count1, int set_prevcount) { if (set_prevcount) { @@ -8716,9 +8353,7 @@ void set_argv_var(char **argv, int argc) set_vim_var_list(VV_ARGV, l); } -/* - * Set v:register if needed. - */ +/// Set v:register if needed. void set_reg_var(int c) { char regname; @@ -8734,12 +8369,10 @@ void set_reg_var(int c) } } -/* - * Get or set v:exception. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:exception! Does not - * take care of memory allocations. - */ +/// Get or set v:exception. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:exception! Does not +/// take care of memory allocations. char_u *v_exception(char_u *oldval) { if (oldval == NULL) { @@ -8750,12 +8383,10 @@ char_u *v_exception(char_u *oldval) return NULL; } -/* - * Get or set v:throwpoint. If "oldval" == NULL, return the current value. - * Otherwise, restore the value to "oldval" and return NULL. - * Must always be called in pairs to save and restore v:throwpoint! Does not - * take care of memory allocations. - */ +/// Get or set v:throwpoint. If "oldval" == NULL, return the current value. +/// Otherwise, restore the value to "oldval" and return NULL. +/// Must always be called in pairs to save and restore v:throwpoint! Does not +/// take care of memory allocations. char_u *v_throwpoint(char_u *oldval) { if (oldval == NULL) { @@ -8766,12 +8397,10 @@ char_u *v_throwpoint(char_u *oldval) return NULL; } -/* - * Set v:cmdarg. - * If "eap" != NULL, use "eap" to generate the value and return the old value. - * If "oldarg" != NULL, restore the value to "oldarg" and return NULL. - * Must always be called in pairs! - */ +/// Set v:cmdarg. +/// If "eap" != NULL, use "eap" to generate the value and return the old value. +/// If "oldarg" != NULL, restore the value to "oldarg" and return NULL. +/// Must always be called in pairs! char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) { char_u *oldval = vimvars[VV_CMDARG].vv_str; @@ -8909,7 +8538,7 @@ static bool tv_is_luafunc(typval_T *tv) const char *skip_luafunc_name(const char *p) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - while (ASCII_ISALNUM(*p) || *p == '_' || *p == '.' || *p == '\'') { + while (ASCII_ISALNUM(*p) || *p == '_' || *p == '-' || *p == '.' || *p == '\'') { p++; } return p; @@ -9036,11 +8665,12 @@ void set_selfdict(typval_T *const rettv, dict_T *const selfdict) make_partial(selfdict, rettv); } -// Find variable "name" in the list of variables. -// Return a pointer to it if found, NULL if not found. -// Careful: "a:0" variables don't have a name. -// When "htp" is not NULL we are writing to the variable, set "htp" to the -// hashtab_T used. +/// Find variable "name" in the list of variables. +/// Careful: "a:0" variables don't have a name. +/// When "htp" is not NULL we are writing to the variable, set "htp" to the +/// hashtab_T used. +/// +/// @return a pointer to it if found, NULL if not found. dictitem_T *find_var(const char *const name, const size_t name_len, hashtab_T **htp, int no_autoload) { @@ -9128,6 +8758,8 @@ dictitem_T *find_var_in_ht(hashtab_T *const ht, int htname, const char *const va /// Finds the dict (g:, l:, s:, …) and hashtable used for a variable. /// +/// Assigns SID if s: scope is accessed from Lua or anonymous Vimscript. #15994 +/// /// @param[in] name Variable name, possibly with scope prefix. /// @param[in] name_len Variable name length. /// @param[out] varname Will be set to the start of the name without scope @@ -9190,10 +8822,32 @@ static hashtab_T *find_var_ht_dict(const char *name, const size_t name_len, cons } else if (*name == 'l' && funccal != NULL) { // local variable *d = &funccal->l_vars; } else if (*name == 's' // script variable - && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR) + && (current_sctx.sc_sid > 0 || current_sctx.sc_sid == SID_STR + || current_sctx.sc_sid == SID_LUA) && current_sctx.sc_sid <= ga_scripts.ga_len) { // For anonymous scripts without a script item, create one now so script vars can be used - if (current_sctx.sc_sid == SID_STR) { + if (current_sctx.sc_sid == SID_LUA) { + // try to resolve lua filename & line no so it can be shown in lastset messages. + nlua_set_sctx(¤t_sctx); + if (current_sctx.sc_sid != SID_LUA) { + // Great we have valid location. Now here this out we'll create a new + // script context with the name and lineno of this one. why ? + // for behavioral consistency. With this different anonymous exec from + // same file can't access each others script local stuff. We need to do + // this all other cases except this will act like that otherwise. + const LastSet last_set = (LastSet){ + .script_ctx = current_sctx, + .channel_id = LUA_INTERNAL_CALL, + }; + bool should_free; + // should_free is ignored as script_sctx will be resolved to a fnmae + // & new_script_item will consume it. + char_u *sc_name = get_scriptname(last_set, &should_free); + new_script_item(sc_name, ¤t_sctx.sc_sid); + } + } + if (current_sctx.sc_sid == SID_STR || current_sctx.sc_sid == SID_LUA) { + // Create SID if s: scope is accessed from Lua or anon Vimscript. #15994 new_script_item(NULL, ¤t_sctx.sc_sid); } *d = &SCRIPT_SV(current_sctx.sc_sid)->sv_dict; @@ -9217,11 +8871,10 @@ hashtab_T *find_var_ht(const char *name, const size_t name_len, const char **var return find_var_ht_dict(name, name_len, varname, &d); } -/* - * Get the string value of a (global/local) variable. - * Note: see tv_get_string() for how long the pointer remains valid. - * Returns NULL when it doesn't exist. - */ +/// @return the string value of a (global/local) variable or +/// NULL when it doesn't exist. +/// +/// @see tv_get_string() for how long the pointer remains valid. char_u *get_var_value(const char *const name) { dictitem_T *v; @@ -9233,16 +8886,14 @@ char_u *get_var_value(const char *const name) return (char_u *)tv_get_string(&v->di_tv); } -/* - * Allocate a new hashtab for a sourced script. It will be used while - * sourcing this script and when executing functions defined in the script. - */ +/// Allocate a new hashtab for a sourced script. It will be used while +/// sourcing this script and when executing functions defined in the script. void new_script_vars(scid_T id) { hashtab_T *ht; scriptvar_T *sv; - ga_grow(&ga_scripts, (int)(id - ga_scripts.ga_len)); + ga_grow(&ga_scripts, id - ga_scripts.ga_len); { /* Re-allocating ga_data means that an ht_array pointing to * ht_smallarray becomes invalid. We can recognize this: ht_mask is @@ -9264,10 +8915,8 @@ void new_script_vars(scid_T id) } } -/* - * Initialize dictionary "dict" as a scope and set variable "dict_var" to - * point to it. - */ +/// Initialize dictionary "dict" as a scope and set variable "dict_var" to +/// point to it. void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) { hash_init(&dict->dv_hashtab); @@ -9283,9 +8932,7 @@ void init_var_dict(dict_T *dict, ScopeDictDictItem *dict_var, int scope) QUEUE_INIT(&dict->watchers); } -/* - * Unreference a dictionary initialized by init_var_dict(). - */ +/// Unreference a dictionary initialized by init_var_dict(). void unref_var_dict(dict_T *dict) { /* Now the dict needs to be freed if no one else is using it, go back to @@ -9294,19 +8941,15 @@ void unref_var_dict(dict_T *dict) tv_dict_unref(dict); } -/* - * Clean up a list of internal variables. - * Frees all allocated variables and the value they contain. - * Clears hashtab "ht", does not free it. - */ +/// Clean up a list of internal variables. +/// Frees all allocated variables and the value they contain. +/// Clears hashtab "ht", does not free it. void vars_clear(hashtab_T *ht) { vars_clear_ext(ht, TRUE); } -/* - * Like vars_clear(), but only free the value if "free_val" is TRUE. - */ +/// Like vars_clear(), but only free the value if "free_val" is TRUE. void vars_clear_ext(hashtab_T *ht, int free_val) { int todo; @@ -9335,10 +8978,8 @@ void vars_clear_ext(hashtab_T *ht, int free_val) ht->ht_used = 0; } -/* - * Delete a variable from hashtab "ht" at item "hi". - * Clear the variable value and free the dictitem. - */ +/// Delete a variable from hashtab "ht" at item "hi". +/// Clear the variable value and free the dictitem. static void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = TV_DICT_HI2DI(hi); @@ -9348,9 +8989,7 @@ static void delete_var(hashtab_T *ht, hashitem_T *hi) xfree(di); } -/* - * List the value of one internal variable. - */ +/// List the value of one internal variable. static void list_one_var(dictitem_T *v, const char *prefix, int *first) { char *const s = encode_tv2echo(&v->di_tv, NULL); @@ -9649,8 +9288,8 @@ bool var_check_func_name(const char *const name, const bool new_var) { // Allow for w: b: s: and t:. if (!(vim_strchr((char_u *)"wbst", name[0]) != NULL && name[1] == ':') - && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') ? name[2] - : name[0])) { + && !ASCII_ISUPPER((name[0] != NUL && name[1] == ':') + ? name[2] : name[0])) { semsg(_("E704: Funcref variable name must start with a capital: %s"), name); return false; } @@ -9783,11 +9422,9 @@ int var_item_copy(const vimconv_T *const conv, typval_T *const from, typval_T *c return ret; } -/* - * ":echo expr1 ..." print each argument separated with a space, add a - * newline at the end. - * ":echon expr1 ..." print each argument plain. - */ +/// ":echo expr1 ..." print each argument separated with a space, add a +/// newline at the end. +/// ":echon expr1 ..." print each argument plain. void ex_echo(exarg_T *eap) { char_u *arg = eap->arg; @@ -9861,21 +9498,17 @@ void ex_echo(exarg_T *eap) } } -/* - * ":echohl {name}". - */ +/// ":echohl {name}". void ex_echohl(exarg_T *eap) { echo_attr = syn_name2attr(eap->arg); } -/* - * ":execute expr1 ..." execute the result of an expression. - * ":echomsg expr1 ..." Print a message - * ":echoerr expr1 ..." Print an error - * Each gets spaces around each argument and a newline at the end for - * echo commands - */ +/// ":execute expr1 ..." execute the result of an expression. +/// ":echomsg expr1 ..." Print a message +/// ":echoerr expr1 ..." Print an error +/// Each gets spaces around each argument and a newline at the end for +/// echo commands void ex_execute(exarg_T *eap) { char_u *arg = eap->arg; @@ -9952,12 +9585,12 @@ void ex_execute(exarg_T *eap) eap->nextcmd = check_nextcmd(arg); } -/* - * Skip over the name of an option: "&option", "&g:option" or "&l:option". - * "arg" points to the "&" or '+' when called, to "option" when returning. - * Returns NULL when no option name found. Otherwise pointer to the char - * after the option name. - */ +/// Skip over the name of an option: "&option", "&g:option" or "&l:option". +/// +/// @param arg points to the "&" or '+' when called, to "option" when returning. +/// +/// @return NULL when no option name found. Otherwise pointer to the char +/// after the option name. static const char *find_option_end(const char **const arg, int *const opt_flags) { const char *p = *arg; @@ -10020,9 +9653,7 @@ void func_do_profile(ufunc_T *fp) fp->uf_profiling = TRUE; } -/* - * Dump the profiling results for all functions in file "fd". - */ +/// Dump the profiling results for all functions in file "fd". void func_dump_profile(FILE *fd) { hashitem_T *hi; @@ -10142,9 +9773,7 @@ static void prof_func_line(FILE *fd, int count, proftime_T *total, proftime_T *s } } -/* - * Compare function for total time sorting. - */ +/// Compare function for total time sorting. static int prof_total_cmp(const void *s1, const void *s2) { ufunc_T *p1 = *(ufunc_T **)s1; @@ -10152,9 +9781,7 @@ static int prof_total_cmp(const void *s1, const void *s2) return profile_cmp(p1->uf_tm_total, p2->uf_tm_total); } -/* - * Compare function for self time sorting. - */ +/// Compare function for self time sorting. static int prof_self_cmp(const void *s1, const void *s2) { ufunc_T *p1 = *(ufunc_T **)s1; @@ -10236,12 +9863,10 @@ bool script_autoload(const char *const name, const size_t name_len, const bool r return ret; } -/* - * Called when starting to read a function line. - * "sourcing_lnum" must be correct! - * When skipping lines it may not actually be executed, but we won't find out - * until later and we need to store the time now. - */ +/// Called when starting to read a function line. +/// "sourcing_lnum" must be correct! +/// When skipping lines it may not actually be executed, but we won't find out +/// until later and we need to store the time now. void func_line_start(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -10261,9 +9886,7 @@ void func_line_start(void *cookie) } } -/* - * Called when actually executing a function line. - */ +/// Called when actually executing a function line. void func_line_exec(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -10274,9 +9897,7 @@ void func_line_exec(void *cookie) } } -/* - * Called when done with a function line. - */ +/// Called when done with a function line. void func_line_end(void *cookie) { funccall_T *fcp = (funccall_T *)cookie; @@ -10411,10 +10032,8 @@ int store_session_globals(FILE *fd) return OK; } -/* - * Display script name where an item was last set. - * Should only be invoked when 'verbose' is non-zero. - */ +/// Display script name where an item was last set. +/// Should only be invoked when 'verbose' is non-zero. void last_set_msg(sctx_T script_ctx) { const LastSet last_set = (LastSet){ @@ -10446,11 +10065,15 @@ void option_last_set_msg(LastSet last_set) } } -// reset v:option_new, v:option_old and v:option_type +// reset v:option_new, v:option_old, v:option_oldlocal, v:option_oldglobal, +// v:option_type, and v:option_command. void reset_v_option_vars(void) { - set_vim_var_string(VV_OPTION_NEW, NULL, -1); - set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_NEW, NULL, -1); + set_vim_var_string(VV_OPTION_OLD, NULL, -1); + set_vim_var_string(VV_OPTION_OLDLOCAL, NULL, -1); + set_vim_var_string(VV_OPTION_OLDGLOBAL, NULL, -1); + set_vim_var_string(VV_OPTION_COMMAND, NULL, -1); set_vim_var_string(VV_OPTION_TYPE, NULL, -1); } @@ -10474,12 +10097,13 @@ int modify_fname(char_u *src, bool tilde_file, size_t *usedlen, char_u **fnamep, char_u *s, *p, *pbuf; char_u dirname[MAXPATHL]; int c; - int has_fullname = 0; + bool has_fullname = false; + bool has_homerelative = false; repeat: // ":p" - full path/file_name if (src[*usedlen] == ':' && src[*usedlen + 1] == 'p') { - has_fullname = 1; + has_fullname = true; valid |= VALID_PATH; *usedlen += 2; @@ -10548,8 +10172,8 @@ repeat: } pbuf = NULL; // Need full path first (use expand_env() to remove a "~/") - if (!has_fullname) { - if (c == '.' && **fnamep == '~') { + if (!has_fullname && !has_homerelative) { + if (**fnamep == '~') { p = pbuf = expand_env_save(*fnamep); } else { p = pbuf = (char_u *)FullName_save((char *)*fnamep, FALSE); @@ -10558,18 +10182,33 @@ repeat: p = *fnamep; } - has_fullname = 0; + has_fullname = false; if (p != NULL) { if (c == '.') { os_dirname(dirname, MAXPATHL); - s = path_shorten_fname(p, dirname); - if (s != NULL) { - *fnamep = s; - if (pbuf != NULL) { - xfree(*bufp); // free any allocated file name - *bufp = pbuf; - pbuf = NULL; + if (has_homerelative) { + s = vim_strsave(dirname); + home_replace(NULL, s, dirname, MAXPATHL, true); + xfree(s); + } + size_t namelen = STRLEN(dirname); + + // Do not call shorten_fname() here since it removes the prefix + // even though the path does not have a prefix. + if (fnamencmp(p, dirname, namelen) == 0) { + p += namelen; + if (vim_ispathsep(*p)) { + while (*p && vim_ispathsep(*p)) { + p++; + } + *fnamep = p; + if (pbuf != NULL) { + // free any allocated file name + xfree(*bufp); + *bufp = pbuf; + pbuf = NULL; + } } } } else { @@ -10580,6 +10219,7 @@ repeat: *fnamep = s; xfree(*bufp); *bufp = s; + has_homerelative = true; } } xfree(pbuf); @@ -10746,7 +10386,8 @@ repeat: /// Perform a substitution on "str" with pattern "pat" and substitute "sub". /// When "sub" is NULL "expr" is used, must be a VAR_FUNC or VAR_PARTIAL. /// "flags" can be "g" to do a global substitute. -/// Returns an allocated string, NULL for error. +/// +/// @return an allocated string, NULL for error. char_u *do_string_sub(char_u *str, char_u *pat, char_u *sub, typval_T *expr, char_u *flags) { int sublen; @@ -10956,10 +10597,7 @@ typval_T eval_call_provider(char *provider, char *method, list_T *arguments, boo bool eval_has_provider(const char *feat) { if (!strequal(feat, "clipboard") - && !strequal(feat, "python") && !strequal(feat, "python3") - && !strequal(feat, "python_compiled") - && !strequal(feat, "python_dynamic") && !strequal(feat, "python3_compiled") && !strequal(feat, "python3_dynamic") && !strequal(feat, "perl") @@ -11085,7 +10723,7 @@ void invoke_prompt_callback(void) tv_clear(&rettv); } -// Return true When the interrupt callback was invoked. +/// @return true when the interrupt callback was invoked. bool invoke_prompt_interrupt(void) { typval_T rettv; |