diff options
Diffstat (limited to 'src/nvim/eval/funcs.c')
-rw-r--r-- | src/nvim/eval/funcs.c | 714 |
1 files changed, 554 insertions, 160 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index 13425b21d1..d7237d6443 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -14,7 +14,6 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> -#include <sys/types.h> #include <time.h> #include <uv.h> @@ -27,9 +26,11 @@ #include "nvim/ascii_defs.h" #include "nvim/assert_defs.h" #include "nvim/autocmd.h" +#include "nvim/autocmd_defs.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" #include "nvim/channel.h" +#include "nvim/channel_defs.h" #include "nvim/charset.h" #include "nvim/cmdexpand.h" #include "nvim/cmdexpand_defs.h" @@ -48,22 +49,26 @@ #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" +#include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" #include "nvim/event/process.h" #include "nvim/event/time.h" #include "nvim/ex_cmds.h" +#include "nvim/ex_cmds_defs.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/garray.h" +#include "nvim/garray_defs.h" #include "nvim/getchar.h" -#include "nvim/gettext.h" +#include "nvim/getchar_defs.h" +#include "nvim/gettext_defs.h" #include "nvim/globals.h" #include "nvim/grid.h" +#include "nvim/grid_defs.h" #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" @@ -75,24 +80,34 @@ #include "nvim/macros_defs.h" #include "nvim/main.h" #include "nvim/mark.h" +#include "nvim/mark_defs.h" #include "nvim/math.h" #include "nvim/mbyte.h" +#include "nvim/mbyte_defs.h" #include "nvim/memline.h" #include "nvim/memory.h" +#include "nvim/memory_defs.h" #include "nvim/menu.h" +#include "nvim/menu_defs.h" #include "nvim/message.h" #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" +#include "nvim/msgpack_rpc/channel_defs.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/normal.h" +#include "nvim/normal_defs.h" #include "nvim/ops.h" #include "nvim/option.h" +#include "nvim/option_defs.h" #include "nvim/option_vars.h" #include "nvim/optionstr.h" #include "nvim/os/dl.h" #include "nvim/os/fileio.h" +#include "nvim/os/fileio_defs.h" #include "nvim/os/fs.h" +#include "nvim/os/fs_defs.h" #include "nvim/os/os.h" +#include "nvim/os/os_defs.h" #include "nvim/os/pty_process.h" #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" @@ -103,15 +118,19 @@ #include "nvim/pos_defs.h" #include "nvim/profile.h" #include "nvim/regexp.h" +#include "nvim/regexp_defs.h" #include "nvim/runtime.h" +#include "nvim/runtime_defs.h" #include "nvim/search.h" #include "nvim/sha256.h" #include "nvim/spell.h" #include "nvim/spellsuggest.h" #include "nvim/state.h" +#include "nvim/state_defs.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/version.h" #include "nvim/vim_defs.h" @@ -305,7 +324,7 @@ int call_internal_method(const char *const fname, const int argcount, typval_T * } /// @return true for a non-zero Number and a non-empty String. -static int non_zero_arg(typval_T *argvars) +static bool non_zero_arg(typval_T *argvars) { return ((argvars[0].v_type == VAR_NUMBER && argvars[0].vval.v_number != 0) @@ -341,33 +360,28 @@ static void api_wrapper(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) MsgpackRpcRequestHandler handler = *fptr.api_handler; - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, false)); } Error err = ERROR_INIT; - Arena res_arena = ARENA_EMPTY; - Object result = handler.fn(VIML_INTERNAL_CALL, args, &res_arena, &err); + Object result = handler.fn(VIML_INTERNAL_CALL, args, &arena, &err); if (ERROR_SET(&err)) { semsg_multiline(e_api_error, err.msg); goto end; } - if (!object_to_vim(result, rettv, &err)) { - assert(ERROR_SET(&err)); - semsg(_("Error converting the call result: %s"), err.msg); - } + object_to_vim_take_luaref(&result, rettv, true, &err); end: - api_free_array(args); - if (handler.arena_return) { - arena_mem_free(arena_finish(&res_arena)); - } else { + if (handler.ret_alloc) { api_free_object(result); } + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -428,8 +442,7 @@ static void f_and(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// "api_info()" function static void f_api_info(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { - Dictionary metadata = api_metadata(); - (void)object_to_vim(DICTIONARY_OBJ(metadata), rettv, NULL); + object_to_vim(api_metadata(), rettv, NULL); } /// "atan2()" function @@ -1022,10 +1035,11 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Dictionary ctx_dict = ctx_to_dict(ctx); + Arena arena = ARENA_EMPTY; + Dictionary ctx_dict = ctx_to_dict(ctx, &arena); Error err = ERROR_INIT; - (void)object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); - api_free_dictionary(ctx_dict); + object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -1093,7 +1107,8 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const int save_did_emsg = did_emsg; did_emsg = false; - Dictionary dict = vim_to_object(&argvars[0]).data.dictionary; + Arena arena = ARENA_EMPTY; + Dictionary dict = vim_to_object(&argvars[0], &arena, true).data.dictionary; Context tmp = CONTEXT_INIT; Error err = ERROR_INIT; ctx_from_dict(dict, &tmp, &err); @@ -1106,7 +1121,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) *ctx = tmp; } - api_free_dictionary(dict); + arena_mem_free(arena_finish(&arena)); api_clear_error(&err); did_emsg = save_did_emsg; } @@ -1675,7 +1690,7 @@ static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) char *path = NULL; - (void)os_can_exe(tv_get_string(&argvars[0]), &path, true); + os_can_exe(tv_get_string(&argvars[0]), &path, true); #ifdef BACKSLASH_IN_FILENAME if (path != NULL) { @@ -1711,7 +1726,7 @@ static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = false; // Trailing garbage. } } else if (*p == '*') { // Internal or user defined function. - n = function_exists(p + 1, false); + n = strnequal(p, "*v:lua.", 7) ? nlua_func_exists(p + 7) : function_exists(p + 1, false); } else if (*p == ':') { n = cmd_exists(p + 1); } else if (*p == '#') { @@ -1922,44 +1937,47 @@ static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, list_T *l1 = argvars[0].vval.v_list; list_T *const l2 = argvars[1].vval.v_list; - if (is_new || !value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { - if (is_new) { - l1 = tv_list_copy(NULL, l1, false, get_copyID()); - if (l1 == NULL) { - return; - } - } - listitem_T *item; - if (argvars[2].v_type != VAR_UNKNOWN) { - int before = (int)tv_get_number_chk(&argvars[2], &error); - if (error) { - return; // Type error; errmsg already given. - } + if (!is_new && value_check_lock(tv_list_locked(l1), arg_errmsg, TV_TRANSLATE)) { + return; + } - if (before == tv_list_len(l1)) { - item = NULL; - } else { - item = tv_list_find(l1, before); - if (item == NULL) { - semsg(_(e_list_index_out_of_range_nr), (int64_t)before); - return; - } - } - } else { - item = NULL; + if (is_new) { + l1 = tv_list_copy(NULL, l1, false, get_copyID()); + if (l1 == NULL) { + return; } - tv_list_extend(l1, l2, item); + } - if (is_new) { - *rettv = (typval_T){ - .v_type = VAR_LIST, - .v_lock = VAR_UNLOCKED, - .vval.v_list = l1, - }; + listitem_T *item; + if (argvars[2].v_type != VAR_UNKNOWN) { + int before = (int)tv_get_number_chk(&argvars[2], &error); + if (error) { + return; // Type error; errmsg already given. + } + + if (before == tv_list_len(l1)) { + item = NULL; } else { - tv_copy(&argvars[0], rettv); + item = tv_list_find(l1, before); + if (item == NULL) { + semsg(_(e_list_index_out_of_range_nr), (int64_t)before); + return; + } } + } else { + item = NULL; + } + tv_list_extend(l1, l2, item); + + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_LIST, + .v_lock = VAR_UNLOCKED, + .vval.v_list = l1, + }; + } else { + tv_copy(&argvars[0], rettv); } } @@ -1970,54 +1988,61 @@ static void extend_list(typval_T *argvars, const char *arg_errmsg, bool is_new, static void extend_dict(typval_T *argvars, const char *arg_errmsg, bool is_new, typval_T *rettv) { dict_T *d1 = argvars[0].vval.v_dict; - dict_T *const d2 = argvars[1].vval.v_dict; if (d1 == NULL) { const bool locked = value_check_lock(VAR_FIXED, arg_errmsg, TV_TRANSLATE); (void)locked; assert(locked == true); - } else if (d2 == NULL) { + return; + } + dict_T *const d2 = argvars[1].vval.v_dict; + if (d2 == NULL) { // Do nothing tv_copy(&argvars[0], rettv); - } else if (is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { - if (is_new) { - d1 = tv_dict_copy(NULL, d1, false, get_copyID()); - if (d1 == NULL) { - return; - } + return; + } + + if (!is_new && value_check_lock(d1->dv_lock, arg_errmsg, TV_TRANSLATE)) { + return; + } + + if (is_new) { + d1 = tv_dict_copy(NULL, d1, false, get_copyID()); + if (d1 == NULL) { + return; } + } - const char *action = "force"; - // Check the third argument. - if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const av[] = { "keep", "force", "error" }; + const char *action = "force"; + // Check the third argument. + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const av[] = { "keep", "force", "error" }; - action = tv_get_string_chk(&argvars[2]); - if (action == NULL) { - return; // Type error; error message already given. - } - size_t i; - for (i = 0; i < ARRAY_SIZE(av); i++) { - if (strcmp(action, av[i]) == 0) { - break; - } - } - if (i == 3) { - semsg(_(e_invarg2), action); - return; + action = tv_get_string_chk(&argvars[2]); + if (action == NULL) { + return; // Type error; error message already given. + } + size_t i; + for (i = 0; i < ARRAY_SIZE(av); i++) { + if (strcmp(action, av[i]) == 0) { + break; } } + if (i == 3) { + semsg(_(e_invarg2), action); + return; + } + } - tv_dict_extend(d1, d2, action); + tv_dict_extend(d1, d2, action); - if (is_new) { - *rettv = (typval_T){ - .v_type = VAR_DICT, - .v_lock = VAR_UNLOCKED, - .vval.v_dict = d1, - }; - } else { - tv_copy(&argvars[0], rettv); - } + if (is_new) { + *rettv = (typval_T){ + .v_type = VAR_DICT, + .v_lock = VAR_UNLOCKED, + .vval.v_dict = d1, + }; + } else { + tv_copy(&argvars[0], rettv); } } @@ -2068,8 +2093,8 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) flags = tv_get_string_buf(&argvars[1], nbuf); } - nvim_feedkeys(cstr_as_string((char *)keys), - cstr_as_string((char *)flags), true); + nvim_feedkeys(cstr_as_string(keys), + cstr_as_string(flags), true); } /// "filereadable()" function @@ -2175,7 +2200,7 @@ static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - if (f <= (float_T) - VARNUMBER_MAX + DBL_EPSILON) { + if (f <= (float_T)(-VARNUMBER_MAX) + DBL_EPSILON) { rettv->vval.v_number = -VARNUMBER_MAX; } else if (f >= (float_T)VARNUMBER_MAX - DBL_EPSILON) { rettv->vval.v_number = VARNUMBER_MAX; @@ -2219,8 +2244,8 @@ static void f_fnamemodify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) len = strlen(fname); if (*mods != NUL) { size_t usedlen = 0; - (void)modify_fname((char *)mods, false, &usedlen, - (char **)&fname, &fbuf, &len); + modify_fname((char *)mods, false, &usedlen, + (char **)&fname, &fbuf, &len); } } @@ -2444,7 +2469,7 @@ static void getpos_both(typval_T *argvars, typval_T *rettv, bool getcurpos, bool : (varnumber_T)0)); tv_list_append_number(l, (fp != NULL) ? (varnumber_T)fp->coladd : (varnumber_T)0); if (getcurpos) { - const int save_set_curswant = curwin->w_set_curswant; + const bool save_set_curswant = curwin->w_set_curswant; const colnr_T save_curswant = curwin->w_curswant; const colnr_T save_virtcol = curwin->w_virtcol; @@ -2776,6 +2801,167 @@ static void f_getpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) getpos_both(argvars, rettv, false, false); } +/// Convert from block_def to string +static char *block_def2str(struct block_def *bd) +{ + size_t size = (size_t)bd->startspaces + (size_t)bd->endspaces + (size_t)bd->textlen; + char *ret = xmalloc(size + 1); + char *p = ret; + memset(p, ' ', (size_t)bd->startspaces); + p += bd->startspaces; + memmove(p, bd->textstart, (size_t)bd->textlen); + p += bd->textlen; + memset(p, ' ', (size_t)bd->endspaces); + *(p + bd->endspaces) = NUL; + return ret; +} + +/// "getregion()" function +static void f_getregion(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + tv_list_alloc_ret(rettv, kListLenMayKnow); + + if (tv_check_for_list_arg(argvars, 0) == FAIL + || tv_check_for_list_arg(argvars, 1) == FAIL + || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { + return; + } + + int fnum1 = -1; + int fnum2 = -1; + pos_T p1, p2; + if (list2fpos(&argvars[0], &p1, &fnum1, NULL, false) != OK + || list2fpos(&argvars[1], &p2, &fnum2, NULL, false) != OK + || fnum1 != fnum2) { + return; + } + + bool is_select_exclusive; + char *type; + char default_type[] = "v"; + if (argvars[2].v_type == VAR_DICT) { + is_select_exclusive = tv_dict_get_bool(argvars[2].vval.v_dict, "exclusive", + *p_sel == 'e'); + type = tv_dict_get_string(argvars[2].vval.v_dict, "type", false); + if (type == NULL) { + type = default_type; + } + } else { + is_select_exclusive = *p_sel == 'e'; + type = default_type; + } + + MotionType region_type = kMTUnknown; + if (type[0] == 'v' && type[1] == NUL) { + region_type = kMTCharWise; + } else if (type[0] == 'V' && type[1] == NUL) { + region_type = kMTLineWise; + } else if (type[0] == Ctrl_V && type[1] == NUL) { + region_type = kMTBlockWise; + } else { + return; + } + + buf_T *const save_curbuf = curbuf; + + if (fnum1 != 0) { + buf_T *findbuf = buflist_findnr(fnum1); + // buffer not loaded + if (findbuf == NULL || findbuf->b_ml.ml_mfp == NULL) { + return; + } + curbuf = findbuf; + } + + const TriState save_virtual = virtual_op; + virtual_op = virtual_active(); + + // NOTE: Adjust is needed. + p1.col--; + p2.col--; + + if (!lt(p1, p2)) { + // swap position + pos_T p = p1; + p1 = p2; + p2 = p; + } + + oparg_T oa; + bool inclusive = true; + + if (region_type == kMTCharWise) { + // handle 'selection' == "exclusive" + if (is_select_exclusive && !equalpos(p1, p2)) { + if (p2.coladd > 0) { + p2.coladd--; + } else if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } else if (p2.lnum > 1) { + p2.lnum--; + p2.col = (colnr_T)strlen(ml_get(p2.lnum)); + if (p2.col > 0) { + p2.col--; + mark_mb_adjustpos(curbuf, &p2); + } + } + } + // if fp2 is on NUL (empty line) inclusive becomes false + if (*ml_get_pos(&p2) == NUL && !virtual_op) { + inclusive = false; + } + } else if (region_type == kMTBlockWise) { + colnr_T sc1, ec1, sc2, ec2; + getvvcol(curwin, &p1, &sc1, NULL, &ec1); + getvvcol(curwin, &p2, &sc2, NULL, &ec2); + oa.motion_type = kMTBlockWise; + oa.inclusive = true; + oa.op_type = OP_NOP; + oa.start = p1; + oa.end = p2; + oa.start_vcol = MIN(sc1, sc2); + if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { + oa.end_vcol = sc2 - 1; + } else { + oa.end_vcol = MAX(ec1, ec2); + } + } + + // Include the trailing byte of a multi-byte char. + int l = utfc_ptr2len(ml_get_pos(&p2)); + if (l > 1) { + p2.col += l - 1; + } + + for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { + char *akt = NULL; + + if (region_type == kMTLineWise) { + akt = xstrdup(ml_get(lnum)); + } else if (region_type == kMTBlockWise) { + struct block_def bd; + block_prep(&oa, &bd, lnum, false); + akt = block_def2str(&bd); + } else if (p1.lnum < lnum && lnum < p2.lnum) { + akt = xstrdup(ml_get(lnum)); + } else { + struct block_def bd; + charwise_block_prep(p1, p2, &bd, lnum, inclusive); + akt = block_def2str(&bd); + } + + assert(akt != NULL); + tv_list_append_allocated_string(rettv->vval.v_list, akt); + } + + if (curbuf != save_curbuf) { + curbuf = save_curbuf; + } + + virtual_op = save_virtual; +} + /// Common between getreg(), getreginfo() and getregtype(): get the register /// name from the first argument. /// Returns zero on error. @@ -3140,7 +3326,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "path_extra", "persistent_undo", "profile", - "pythonx", "reltime", "quickfix", "rightleft", @@ -3182,6 +3367,7 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) "xattr", #endif "nvim", + "rneovim", }; // XXX: eval_has_provider() may shell out :( @@ -3231,6 +3417,8 @@ static void f_has(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) n = syntax_present(curwin); } else if (STRICMP(name, "clipboard_working") == 0) { n = eval_has_provider("clipboard"); + } else if (STRICMP(name, "pythonx") == 0) { + n = eval_has_provider("python3"); } else if (STRICMP(name, "wsl") == 0) { n = has_wsl(); #ifdef UNIX @@ -3253,13 +3441,11 @@ static bool has_wsl(void) static TriState has_wsl = kNone; if (has_wsl == kNone) { Error err = ERROR_INIT; - Object o = nlua_exec(STATIC_CSTR_AS_STRING("return vim.uv.os_uname()['release']:lower()" - ":match('microsoft') and true or false"), - (Array)ARRAY_DICT_INIT, &err); + Object o = NLUA_EXEC_STATIC("return vim.uv.os_uname()['release']:lower()" + ":match('microsoft')", + (Array)ARRAY_DICT_INIT, kRetNilBool, NULL, &err); assert(!ERROR_SET(&err)); - assert(o.type == kObjectTypeBoolean); - has_wsl = o.data.boolean ? kTrue : kFalse; - api_free_object(o); + has_wsl = LUARET_TRUTHY(o) ? kTrue : kFalse; } return has_wsl == kTrue; } @@ -3639,7 +3825,7 @@ static void f_inputlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) }); // Ask for choice. - int mouse_used; + bool mouse_used; int selected = prompt_for_number(&mouse_used); if (mouse_used) { selected -= lines_left; @@ -3899,13 +4085,16 @@ static void f_jobresize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_number = 1; } -static const char *ignored_env_vars[] = { +static const char *pty_ignored_env_vars[] = { #ifndef MSWIN "COLUMNS", "LINES", "TERMCAP", "COLORFGBG", + "COLORTERM", #endif + "VIM", + "VIMRUNTIME", NULL }; @@ -3944,9 +4133,9 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en // child process. We're removing them here so the user can still decide // they want to explicitly set them. for (size_t i = 0; - i < ARRAY_SIZE(ignored_env_vars) && ignored_env_vars[i]; + i < ARRAY_SIZE(pty_ignored_env_vars) && pty_ignored_env_vars[i]; i++) { - dictitem_T *dv = tv_dict_find(env, ignored_env_vars[i], -1); + dictitem_T *dv = tv_dict_find(env, pty_ignored_env_vars[i], -1); if (dv) { tv_dict_item_remove(env, dv); } @@ -3954,10 +4143,6 @@ static dict_T *create_environment(const dictitem_T *job_env, const bool clear_en #ifndef MSWIN // Set COLORTERM to "truecolor" if termguicolors is set if (p_tgc) { - dictitem_T *dv = tv_dict_find(env, S_LEN("COLORTERM")); - if (dv) { - tv_dict_item_remove(env, dv); - } tv_dict_add_str(env, S_LEN("COLORTERM"), "truecolor"); } #endif @@ -4056,8 +4241,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) bool clear_env = false; bool overlapped = false; ChannelStdinMode stdin_mode = kChannelStdinPipe; - CallbackReader on_stdout = CALLBACK_READER_INIT, - on_stderr = CALLBACK_READER_INIT; + CallbackReader on_stdout = CALLBACK_READER_INIT; + CallbackReader on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; char *cwd = NULL; dictitem_T *job_env = NULL; @@ -4120,7 +4305,8 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } - uint16_t width = 0, height = 0; + uint16_t width = 0; + uint16_t height = 0; char *term_name = NULL; if (pty) { @@ -4168,7 +4354,7 @@ static void f_jobstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) const char *error = NULL; if (data->is_rpc) { // Ignore return code, but show error later. - (void)channel_close(data->id, kChannelPartRpc, &error); + channel_close(data->id, kChannelPartRpc, &error); } process_stop(&data->stream.proc); rettv->vval.v_number = 1; @@ -4503,7 +4689,7 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - nlua_typval_eval(cstr_as_string((char *)str), &argvars[1], rettv); + nlua_typval_eval(cstr_as_string(str), &argvars[1], rettv); } static void find_some_match(typval_T *const argvars, typval_T *const rettv, @@ -4714,6 +4900,151 @@ theend: p_cpo = save_cpo; } +/// Return all the matches in string "str" for pattern "rmp". +/// The matches are returned in the List "mlist". +/// If "submatches" is true, then submatch information is also returned. +/// "matchbuf" is true when called for matchbufline(). +static void get_matches_in_str(const char *str, regmatch_T *rmp, list_T *mlist, int idx, + bool submatches, bool matchbuf) +{ + size_t len = strlen(str); + int match = 0; + colnr_T startidx = 0; + + while (true) { + match = vim_regexec_nl(rmp, str, startidx); + if (!match) { + break; + } + + dict_T *d = tv_dict_alloc(); + tv_list_append_dict(mlist, d); + + if (matchbuf) { + tv_dict_add_nr(d, S_LEN("lnum"), idx); + } else { + tv_dict_add_nr(d, S_LEN("idx"), idx); + } + + tv_dict_add_nr(d, S_LEN("byteidx"), + (colnr_T)(rmp->startp[0] - str)); + + tv_dict_add_str_len(d, S_LEN("text"), rmp->startp[0], + (int)(rmp->endp[0] - rmp->startp[0])); + + if (submatches) { + list_T *sml = tv_list_alloc(NSUBEXP - 1); + + tv_dict_add_list(d, S_LEN("submatches"), sml); + + // return a list with the submatches + for (int i = 1; i < NSUBEXP; i++) { + if (rmp->endp[i] == NULL) { + tv_list_append_string(sml, "", 0); + } else { + tv_list_append_string(sml, rmp->startp[i], rmp->endp[i] - rmp->startp[i]); + } + } + } + startidx = (colnr_T)(rmp->endp[0] - str); + if (startidx >= (colnr_T)len || str + startidx <= rmp->startp[0]) { + break; + } + } +} + +/// "matchbufline()" function +static void f_matchbufline(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *retlist = rettv->vval.v_list; + + if (tv_check_for_buffer_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_lnum_arg(argvars, 2) == FAIL + || tv_check_for_lnum_arg(argvars, 3) == FAIL + || tv_check_for_opt_dict_arg(argvars, 4) == FAIL) { + return; + } + + const int prev_did_emsg = did_emsg; + buf_T *buf = tv_get_buf(&argvars[0], false); + if (buf == NULL) { + if (did_emsg == prev_did_emsg) { + semsg(_(e_invalid_buffer_name_str), tv_get_string(&argvars[0])); + } + return; + } + if (buf->b_ml.ml_mfp == NULL) { + emsg(_(e_buffer_is_not_loaded)); + return; + } + + char patbuf[NUMBUFLEN]; + const char *pat = tv_get_string_buf(&argvars[1], patbuf); + + const int did_emsg_before = did_emsg; + linenr_T slnum = tv_get_lnum_buf(&argvars[2], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (slnum < 1) { + semsg(_(e_invargval), "lnum"); + return; + } + + linenr_T elnum = tv_get_lnum_buf(&argvars[3], buf); + if (did_emsg > did_emsg_before) { + return; + } + if (elnum < 1 || elnum < slnum) { + semsg(_(e_invargval), "end_lnum"); + return; + } + + if (elnum > buf->b_ml.ml_line_count) { + elnum = buf->b_ml.ml_line_count; + } + + bool submatches = false; + if (argvars[4].v_type != VAR_UNKNOWN) { + dict_T *d = argvars[4].vval.v_dict; + if (d != NULL) { + dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); + if (di != NULL) { + if (di->di_tv.v_type != VAR_BOOL) { + semsg(_(e_invargval), "submatches"); + return; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + char *const save_cpo = p_cpo; + p_cpo = empty_string_option; + + regmatch_T regmatch; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) { + goto theend; + } + regmatch.rm_ic = p_ic; + + while (slnum <= elnum) { + const char *str = ml_get_buf(buf, slnum); + get_matches_in_str(str, ®match, retlist, slnum, submatches, true); + slnum++; + } + + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + /// "match()" function static void f_match(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -4738,6 +5069,73 @@ static void f_matchstr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) find_some_match(argvars, rettv, kSomeMatchStr); } +/// "matchstrlist()" function +static void f_matchstrlist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + rettv->vval.v_number = -1; + tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *retlist = rettv->vval.v_list; + + if (tv_check_for_list_arg(argvars, 0) == FAIL + || tv_check_for_string_arg(argvars, 1) == FAIL + || tv_check_for_opt_dict_arg(argvars, 2) == FAIL) { + return; + } + + list_T *l = NULL; + if ((l = argvars[0].vval.v_list) == NULL) { + return; + } + + char patbuf[NUMBUFLEN]; + const char *pat = tv_get_string_buf_chk(&argvars[1], patbuf); + if (pat == NULL) { + return; + } + + // Make 'cpoptions' empty, the 'l' flag should not be used here. + char *const save_cpo = p_cpo; + p_cpo = empty_string_option; + + regmatch_T regmatch; + regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING); + if (regmatch.regprog == NULL) { + goto theend; + } + regmatch.rm_ic = p_ic; + + bool submatches = false; + if (argvars[2].v_type != VAR_UNKNOWN) { + dict_T *d = argvars[2].vval.v_dict; + if (d != NULL) { + dictitem_T *di = tv_dict_find(d, S_LEN("submatches")); + if (di != NULL) { + if (di->di_tv.v_type != VAR_BOOL) { + semsg(_(e_invargval), "submatches"); + goto cleanup; + } + submatches = tv_get_bool(&di->di_tv); + } + } + } + + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + const typval_T *const li_tv = TV_LIST_ITEM_TV(li); + if (li_tv->v_type == VAR_STRING && li_tv->vval.v_string != NULL) { + const char *str = li_tv->vval.v_string; + get_matches_in_str(str, ®match, retlist, idx, submatches, false); + } + idx++; + }); + +cleanup: + vim_regfree(regmatch.regprog); + +theend: + p_cpo = save_cpo; +} + /// "matchstrpos()" function static void f_matchstrpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -5216,7 +5614,7 @@ static void f_printf(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (!did_emsg) { char *s = xmalloc((size_t)len + 1); rettv->vval.v_string = s; - (void)vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); + vim_vsnprintf_typval(s, (size_t)len + 1, fmt, dummy_ap, argvars + 1); } did_emsg |= saved_did_emsg; } @@ -5620,6 +6018,12 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } } + if (blob) { + tv_blob_alloc_ret(rettv); + } else { + tv_list_alloc_ret(rettv, kListLenUnknown); + } + // Always open the file in binary mode, library functions have a mind of // their own about CR-LF conversion. const char *const fname = tv_get_string(&argvars[0]); @@ -5634,7 +6038,6 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl } if (blob) { - tv_blob_alloc_ret(rettv); if (read_blob(fd, rettv, offset, size) == FAIL) { semsg(_(e_notread), fname); } @@ -5642,7 +6045,7 @@ static void read_file_or_blob(typval_T *argvars, typval_T *rettv, bool always_bl return; } - list_T *const l = tv_list_alloc_ret(rettv, kListLenUnknown); + list_T *const l = rettv->vval.v_list; while (maxline < 0 || tv_list_len(l) < maxline) { int readlen = (int)fread(buf, 1, (size_t)io_size, fd); @@ -5807,7 +6210,7 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (list == NULL) { return; } - (void)tv_dict_add_list(dict, S_LEN("regcontents"), list); + tv_dict_add_list(dict, S_LEN("regcontents"), list); char buf[NUMBUFLEN + 2]; buf[0] = NUL; @@ -5826,15 +6229,15 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) case kMTUnknown: abort(); } - (void)tv_dict_add_str(dict, S_LEN("regtype"), buf); + tv_dict_add_str(dict, S_LEN("regtype"), buf); buf[0] = (char)get_register_name(get_unname_register()); buf[1] = NUL; if (regname == '"') { - (void)tv_dict_add_str(dict, S_LEN("points_to"), buf); + tv_dict_add_str(dict, S_LEN("points_to"), buf); } else { - (void)tv_dict_add_bool(dict, S_LEN("isunnamed"), - regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); + tv_dict_add_bool(dict, S_LEN("isunnamed"), + regname == buf[0] ? kBoolVarTrue : kBoolVarFalse); } } @@ -6648,16 +7051,17 @@ static void f_rpcnotify(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } bool ok = rpc_send_event((uint64_t)argvars[0].vval.v_number, tv_get_string(&argvars[1]), args); - api_free_array(args); + arena_mem_free(arena_finish(&arena)); if (!ok) { semsg(_(e_invarg2), "Channel doesn't exist"); @@ -6687,10 +7091,11 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - Array args = ARRAY_DICT_INIT; + MAXSIZE_TEMP_ARRAY(args, MAX_FUNC_ARGS); + Arena arena = ARENA_EMPTY; for (typval_T *tv = argvars + 2; tv->v_type != VAR_UNKNOWN; tv++) { - ADD(args, vim_to_object(tv)); + ADD_C(args, vim_to_object(tv, &arena, true)); } sctx_T save_current_sctx; @@ -6726,6 +7131,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ArenaMem res_mem = NULL; Object result = rpc_send_call(chan_id, method, args, &res_mem, &err); + arena_mem_free(arena_finish(&arena)); if (l_provider_call_nesting) { current_sctx = save_current_sctx; @@ -6755,10 +7161,7 @@ static void f_rpcrequest(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) goto end; } - if (!object_to_vim(result, rettv, &err)) { - assert(ERROR_SET(&err)); - semsg(_("Error converting the call result: %s"), err.msg); - } + object_to_vim(result, rettv, &err); end: arena_mem_free(res_mem); @@ -6889,15 +7292,8 @@ static void f_screenchar(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) ScreenGrid *grid; screenchar_adjust(&grid, &row, &col); - int c; - if (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) { - c = -1; - } else { - char buf[MAX_SCHAR_SIZE + 1]; - schar_get(buf, grid_getchar(grid, row, col, NULL)); - c = utf_ptr2char(buf); - } - rettv->vval.v_number = c; + rettv->vval.v_number = (row < 0 || row >= grid->rows || col < 0 || col >= grid->cols) + ? -1 : schar_get_first_codepoint(grid_getchar(grid, row, col, NULL)); } /// "screenchars()" function @@ -7247,7 +7643,7 @@ int do_searchpair(const char *spat, const char *mpat, const char *epat, int dir, // If it's still empty it was changed and restored, need to restore in // the complicated way. if (*p_cpo == NUL) { - set_option_value_give_err("cpo", CSTR_AS_OPTVAL(save_cpo), 0); + set_option_value_give_err(kOptCpoptions, CSTR_AS_OPTVAL(save_cpo), 0); } free_string_option(save_cpo); } @@ -8090,7 +8486,7 @@ static void f_str2float(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (*p == '+' || *p == '-') { p = skipwhite(p + 1); } - (void)string2float(p, &rettv->vval.v_float); + string2float(p, &rettv->vval.v_float); if (isneg) { rettv->vval.v_float *= -1; } @@ -8391,7 +8787,6 @@ static void f_synIDtrans(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { int syntax_flags = 0; - int cchar; int matchid = 0; char str[NUMBUFLEN]; @@ -8405,19 +8800,18 @@ static void f_synconcealed(typval_T *argvars, typval_T *rettv, EvalFuncData fptr if (lnum >= 1 && lnum <= curbuf->b_ml.ml_line_count && col >= 0 && (size_t)col <= strlen(ml_get(lnum)) && curwin->w_p_cole > 0) { - (void)syn_get_id(curwin, lnum, col, false, NULL, false); + syn_get_id(curwin, lnum, col, false, NULL, false); syntax_flags = get_syntax_info(&matchid); // get the conceal character if ((syntax_flags & HL_CONCEAL) && curwin->w_p_cole < 3) { - cchar = syn_get_sub_char(); + schar_T cchar = schar_from_char(syn_get_sub_char()); if (cchar == NUL && curwin->w_p_cole == 1) { cchar = (curwin->w_p_lcs_chars.conceal == NUL) - ? ' ' - : curwin->w_p_lcs_chars.conceal; + ? schar_from_ascii(' ') : curwin->w_p_lcs_chars.conceal; } if (cchar != NUL) { - utf_char2bytes(cchar, str); + schar_get(str, cchar); } } } @@ -8443,7 +8837,7 @@ static void f_synstack(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) && col >= 0 && (size_t)col <= strlen(ml_get(lnum))) { tv_list_alloc_ret(rettv, kListLenMayKnow); - (void)syn_get_id(curwin, lnum, col, false, NULL, true); + syn_get_id(curwin, lnum, col, false, NULL, true); int id; int i = 0; @@ -8517,8 +8911,8 @@ static void f_taglist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) if (argvars[1].v_type != VAR_UNKNOWN) { fname = tv_get_string(&argvars[1]); } - (void)get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), - (char *)tag_pattern, (char *)fname); + get_tags(tv_list_alloc_ret(rettv, kListLenUnknown), + (char *)tag_pattern, (char *)fname); } /// "tempname()" function @@ -8558,8 +8952,8 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) return; } - CallbackReader on_stdout = CALLBACK_READER_INIT, - on_stderr = CALLBACK_READER_INIT; + CallbackReader on_stdout = CALLBACK_READER_INIT; + CallbackReader on_stderr = CALLBACK_READER_INIT; Callback on_exit = CALLBACK_NONE; dict_T *job_opts = NULL; const char *cwd = "."; @@ -8637,17 +9031,17 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) curbuf->b_p_swf = false; apply_autocmds(EVENT_BUFFILEPRE, NULL, NULL, false, curbuf); - (void)setfname(curbuf, NameBuff, NULL, true); + setfname(curbuf, NameBuff, NULL, true); apply_autocmds(EVENT_BUFFILEPOST, NULL, NULL, false, curbuf); // Save the job id and pid in b:terminal_job_{id,pid} Error err = ERROR_INIT; // deprecated: use 'channel' buffer option dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_id"), - INTEGER_OBJ((Integer)chan->id), false, false, &err); + INTEGER_OBJ((Integer)chan->id), false, false, NULL, &err); api_clear_error(&err); dict_set_var(curbuf->b_vars, cstr_as_string("terminal_job_pid"), - INTEGER_OBJ(pid), false, false, &err); + INTEGER_OBJ(pid), false, false, NULL, &err); api_clear_error(&err); channel_incref(chan); |