diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2024-11-19 22:57:13 +0000 |
| commit | 9be89f131f87608f224f0ee06d199fcd09d32176 (patch) | |
| tree | 11022dcfa9e08cb4ac5581b16734196128688d48 /src/nvim/eval/funcs.c | |
| parent | ff7ed8f586589d620a806c3758fac4a47a8e7e15 (diff) | |
| parent | 88085c2e80a7e3ac29aabb6b5420377eed99b8b6 (diff) | |
| download | rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.gz rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.tar.bz2 rneovim-9be89f131f87608f224f0ee06d199fcd09d32176.zip | |
Merge remote-tracking branch 'upstream/master' into mix_20240309
Diffstat (limited to 'src/nvim/eval/funcs.c')
| -rw-r--r-- | src/nvim/eval/funcs.c | 1811 |
1 files changed, 382 insertions, 1429 deletions
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index e3afc1cf54..6d1cb4b2c3 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -1,23 +1,19 @@ #include <assert.h> -#include <fcntl.h> #include <float.h> #include <inttypes.h> #include <limits.h> #include <math.h> -#include <msgpack/object.h> -#include <msgpack/pack.h> -#include <msgpack/unpack.h> #include <signal.h> #include <stdarg.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/stat.h> #include <time.h> #include <uv.h> #include "auto/config.h" +#include "mpack/object.h" #include "nvim/api/private/converter.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/dispatch.h" @@ -38,6 +34,7 @@ #include "nvim/cursor.h" #include "nvim/diff.h" #include "nvim/edit.h" +#include "nvim/errors.h" #include "nvim/eval.h" #include "nvim/eval/buffer.h" #include "nvim/eval/decode.h" @@ -52,15 +49,13 @@ #include "nvim/event/defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" -#include "nvim/event/process.h" +#include "nvim/event/proc.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/garray.h" #include "nvim/garray_defs.h" #include "nvim/getchar.h" @@ -93,6 +88,7 @@ #include "nvim/move.h" #include "nvim/msgpack_rpc/channel.h" #include "nvim/msgpack_rpc/channel_defs.h" +#include "nvim/msgpack_rpc/packer.h" #include "nvim/msgpack_rpc/server.h" #include "nvim/normal.h" #include "nvim/normal_defs.h" @@ -102,13 +98,10 @@ #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/pty_proc.h" #include "nvim/os/shell.h" #include "nvim/os/stdpaths_defs.h" #include "nvim/os/time.h" @@ -132,6 +125,7 @@ #include "nvim/tag.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -772,41 +766,6 @@ static void f_charcol(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) get_col(argvars, rettv, true); } -/// "chdir(dir)" function -static void f_chdir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - if (argvars[0].v_type != VAR_STRING) { - // Returning an empty string means it failed. - // No error message, for historic reasons. - return; - } - - // Return the current directory - char *cwd = xmalloc(MAXPATHL); - if (os_dirname(cwd, MAXPATHL) != FAIL) { -#ifdef BACKSLASH_IN_FILENAME - slash_adjust(cwd); -#endif - rettv->vval.v_string = xstrdup(cwd); - } - xfree(cwd); - - CdScope scope = kCdScopeGlobal; - if (curwin->w_localdir != NULL) { - scope = kCdScopeWindow; - } else if (curtab->tp_localdir != NULL) { - scope = kCdScopeTabpage; - } - - if (!changedir_func(argvars[0].vval.v_string, scope)) { - // Directory change failed - XFREE_CLEAR(rettv->vval.v_string); - } -} - /// "cindent(lnum)" function static void f_cindent(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -951,7 +910,7 @@ static varnumber_T count_list(list_T *l, typval_T *needle, int64_t idx, bool ic) varnumber_T n = 0; for (; li != NULL; li = TV_LIST_ITEM_NEXT(l, li)) { - if (tv_equal(TV_LIST_ITEM_TV(li), needle, ic, false)) { + if (tv_equal(TV_LIST_ITEM_TV(li), needle, ic)) { n++; } } @@ -971,7 +930,7 @@ static varnumber_T count_dict(dict_T *d, typval_T *needle, bool ic) varnumber_T n = 0; TV_DICT_ITER(d, di, { - if (tv_equal(&di->di_tv, needle, ic, false)) { + if (tv_equal(&di->di_tv, needle, ic)) { n++; } }); @@ -1036,9 +995,9 @@ static void f_ctxget(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } Arena arena = ARENA_EMPTY; - Dictionary ctx_dict = ctx_to_dict(ctx, &arena); + Dict ctx_dict = ctx_to_dict(ctx, &arena); Error err = ERROR_INIT; - object_to_vim(DICTIONARY_OBJ(ctx_dict), rettv, &err); + object_to_vim(DICT_OBJ(ctx_dict), rettv, &err); arena_mem_free(arena_finish(&arena)); api_clear_error(&err); } @@ -1108,7 +1067,7 @@ static void f_ctxset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) did_emsg = false; Arena arena = ARENA_EMPTY; - Dictionary dict = vim_to_object(&argvars[0], &arena, true).data.dictionary; + Dict dict = vim_to_object(&argvars[0], &arena, true).data.dict; Context tmp = CONTEXT_INIT; Error err = ERROR_INIT; ctx_from_dict(dict, &tmp, &err); @@ -1250,42 +1209,6 @@ static void f_deepcopy(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) var_item_copy(NULL, &argvars[0], rettv, true, (noref == 0 ? get_copyID() : 0)); } -/// "delete()" function -static void f_delete(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - rettv->vval.v_number = -1; - if (check_secure()) { - return; - } - - const char *const name = tv_get_string(&argvars[0]); - if (*name == NUL) { - emsg(_(e_invarg)); - return; - } - - char nbuf[NUMBUFLEN]; - const char *flags; - if (argvars[1].v_type != VAR_UNKNOWN) { - flags = tv_get_string_buf(&argvars[1], nbuf); - } else { - flags = ""; - } - - if (*flags == NUL) { - // delete a file - rettv->vval.v_number = os_remove(name) == 0 ? 0 : -1; - } else if (strcmp(flags, "d") == 0) { - // delete an empty directory - rettv->vval.v_number = os_rmdir(name) == 0 ? 0 : -1; - } else if (strcmp(flags, "rf") == 0) { - // delete a directory recursively - rettv->vval.v_number = delete_recursive(name); - } else { - semsg(_(e_invexpr2), flags); - } -} - /// dictwatcheradd(dict, key, funcref) function static void f_dictwatcheradd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -1568,17 +1491,6 @@ static void f_eventhandler(typval_T *argvars, typval_T *rettv, EvalFuncData fptr rettv->vval.v_number = vgetc_busy; } -/// "executable()" function -static void f_executable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - if (tv_check_for_string_arg(argvars, 0) == FAIL) { - return; - } - - // Check in $PATH and also check directly if there is a directory name - rettv->vval.v_number = os_can_exe(tv_get_string(&argvars[0]), NULL, true); -} - typedef struct { const list_T *const l; const listitem_T *li; @@ -1682,27 +1594,6 @@ static void f_execute(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) execute_common(argvars, rettv, 0); } -/// "exepath()" function -static void f_exepath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - if (tv_check_for_nonempty_string_arg(argvars, 0) == FAIL) { - return; - } - - char *path = NULL; - - os_can_exe(tv_get_string(&argvars[0]), &path, true); - -#ifdef BACKSLASH_IN_FILENAME - if (path != NULL) { - slash_adjust(path); - } -#endif - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = path; -} - /// "exists()" function static void f_exists(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -2104,100 +1995,6 @@ static void f_feedkeys(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) cstr_as_string(flags), true); } -/// "filereadable()" function -static void f_filereadable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - const char *const p = tv_get_string(&argvars[0]); - rettv->vval.v_number = - (*p && !os_isdir(p) && os_file_is_readable(p)); -} - -/// @return 0 for not writable -/// 1 for writable file -/// 2 for a dir which we have rights to write into. -static void f_filewritable(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - const char *filename = tv_get_string(&argvars[0]); - rettv->vval.v_number = os_file_is_writable(filename); -} - -static void findfilendir(typval_T *argvars, typval_T *rettv, int find_what) -{ - char *fresult = NULL; - char *path = *curbuf->b_p_path == NUL ? p_path : curbuf->b_p_path; - int count = 1; - bool first = true; - bool error = false; - - rettv->vval.v_string = NULL; - rettv->v_type = VAR_STRING; - - const char *fname = tv_get_string(&argvars[0]); - - char pathbuf[NUMBUFLEN]; - if (argvars[1].v_type != VAR_UNKNOWN) { - const char *p = tv_get_string_buf_chk(&argvars[1], pathbuf); - if (p == NULL) { - error = true; - } else { - if (*p != NUL) { - path = (char *)p; - } - - if (argvars[2].v_type != VAR_UNKNOWN) { - count = (int)tv_get_number_chk(&argvars[2], &error); - } - } - } - - if (count < 0) { - tv_list_alloc_ret(rettv, kListLenUnknown); - } - - if (*fname != NUL && !error) { - char *file_to_find = NULL; - char *search_ctx = NULL; - - do { - if (rettv->v_type == VAR_STRING || rettv->v_type == VAR_LIST) { - xfree(fresult); - } - fresult = find_file_in_path_option(first ? (char *)fname : NULL, - first ? strlen(fname) : 0, - 0, first, path, - find_what, curbuf->b_ffname, - (find_what == FINDFILE_DIR - ? "" - : curbuf->b_p_sua), - &file_to_find, &search_ctx); - first = false; - - if (fresult != NULL && rettv->v_type == VAR_LIST) { - tv_list_append_string(rettv->vval.v_list, fresult, -1); - } - } while ((rettv->v_type == VAR_LIST || --count > 0) && fresult != NULL); - - xfree(file_to_find); - vim_findfile_cleanup(search_ctx); - } - - if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = fresult; - } -} - -/// "finddir({fname}[, {path}[, {count}]])" function -static void f_finddir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - findfilendir(argvars, rettv, FINDFILE_DIR); -} - -/// "findfile({fname}[, {path}[, {count}]])" function -static void f_findfile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - findfilendir(argvars, rettv, FINDFILE_FILE); -} - /// "float2nr({float})" function static void f_float2nr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -2270,6 +2067,164 @@ static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { } +/// "function()" function +/// "funcref()" function +static void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) +{ + char *s; + char *name; + bool use_string = false; + partial_T *arg_pt = NULL; + char *trans_name = NULL; + + if (argvars[0].v_type == VAR_FUNC) { + // function(MyFunc, [arg], dict) + s = argvars[0].vval.v_string; + } else if (argvars[0].v_type == VAR_PARTIAL + && argvars[0].vval.v_partial != NULL) { + // function(dict.MyFunc, [arg]) + arg_pt = argvars[0].vval.v_partial; + s = partial_name(arg_pt); + // TODO(bfredl): do the entire nlua_is_table_from_lua dance + } else { + // function('MyFunc', [arg], dict) + s = (char *)tv_get_string(&argvars[0]); + use_string = true; + } + + if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { + name = s; + trans_name = save_function_name(&name, false, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); + if (*name != NUL) { + s = NULL; + } + } + if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) + || (is_funcref && trans_name == NULL)) { + semsg(_(e_invarg2), (use_string ? tv_get_string(&argvars[0]) : 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(trans_name))) { + semsg(_("E700: Unknown function: %s"), s); + } else { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { + // Expand s: and <SID> into <SNR>nr_, so that the function can + // also be called from another script. Using trans_function_name() + // would also work, but some plugins depend on the name being + // printable text. + name = get_scriptlocal_funcname(s); + } else { + name = xstrdup(s); + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[2].v_type != VAR_UNKNOWN) { + // function(name, [args], dict) + arg_idx = 1; + dict_idx = 2; + } else if (argvars[1].v_type == VAR_DICT) { + // function(name, dict) + dict_idx = 1; + } else { + // function(name, [args]) + arg_idx = 1; + } + if (dict_idx > 0) { + if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) { + xfree(name); + goto theend; + } + if (argvars[dict_idx].vval.v_dict == NULL) { + dict_idx = 0; + } + } + if (arg_idx > 0) { + if (argvars[arg_idx].v_type != VAR_LIST) { + emsg(_("E923: Second argument of function() must be " + "a list or a dict")); + xfree(name); + goto theend; + } + list = argvars[arg_idx].vval.v_list; + if (tv_list_len(list) == 0) { + arg_idx = 0; + } else if (tv_list_len(list) > MAX_FUNC_ARGS) { + emsg_funcname(e_toomanyarg, s); + xfree(name); + goto theend; + } + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { + partial_T *const pt = xcalloc(1, sizeof(*pt)); + + // result is a VAR_PARTIAL + if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { + const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); + const int lv_len = tv_list_len(list); + + pt->pt_argc = arg_len + lv_len; + pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); + int i = 0; + for (; i < arg_len; i++) { + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + } + if (lv_len > 0) { + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); + } + } + + // For "function(dict.func, [], dict)" and "func" is a partial + // use "dict". That is backwards compatible. + if (dict_idx > 0) { + // The dict is bound explicitly, pt_auto is false + pt->pt_dict = argvars[dict_idx].vval.v_dict; + (pt->pt_dict->dv_refcount)++; + } else if (arg_pt != NULL) { + // If the dict was bound automatically the result is also + // bound automatically. + pt->pt_dict = arg_pt->pt_dict; + pt->pt_auto = arg_pt->pt_auto; + if (pt->pt_dict != NULL) { + (pt->pt_dict->dv_refcount)++; + } + } + + pt->pt_refcount = 1; + if (arg_pt != NULL && arg_pt->pt_func != NULL) { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + xfree(name); + } else if (is_funcref) { + pt->pt_func = find_func(trans_name); + func_ptr_ref(pt->pt_func); + xfree(name); + } else { + pt->pt_name = name; + func_ref(name); + } + + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } else { + // result is a VAR_FUNC + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref(name); + } + } +theend: + xfree(trans_name); +} + static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { common_function(argvars, rettv, true); @@ -2370,6 +2325,33 @@ static void f_get(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) for (int i = 0; i < pt->pt_argc; i++) { tv_list_append_tv(rettv->vval.v_list, &pt->pt_argv[i]); } + } else if (strcmp(what, "arity") == 0) { + int required = 0; + int optional = 0; + bool varargs = false; + const char *name = partial_name(pt); + + get_func_arity(name, &required, &optional, &varargs); + + rettv->v_type = VAR_DICT; + tv_dict_alloc_ret(rettv); + dict_T *dict = rettv->vval.v_dict; + + // Take into account the arguments of the partial, if any. + // Note that it is possible to supply more arguments than the function + // accepts. + if (pt->pt_argc >= required + optional) { + required = optional = 0; + } else if (pt->pt_argc > required) { + optional -= pt->pt_argc - required; + required = 0; + } else { + required -= pt->pt_argc; + } + + tv_dict_add_nr(dict, S_LEN("required"), required); + tv_dict_add_nr(dict, S_LEN("optional"), optional); + tv_dict_add_bool(dict, S_LEN("varargs"), varargs); } else { semsg(_(e_invarg2), what); } @@ -2516,127 +2498,6 @@ static void f_getcharsearch(typval_T *argvars, typval_T *rettv, EvalFuncData fpt tv_dict_add_nr(dict, S_LEN("until"), last_csearch_until()); } -/// `getcwd([{win}[, {tab}]])` function -/// -/// Every scope not specified implies the currently selected scope object. -/// -/// @pre The arguments must be of type number. -/// @pre There may not be more than two arguments. -/// @pre An argument may not be -1 if preceding arguments are not all -1. -/// -/// @post The return value will be a string. -static void f_getcwd(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - // Possible scope of working directory to return. - CdScope scope = kCdScopeInvalid; - - // Numbers of the scope objects (window, tab) we want the working directory - // of. A `-1` means to skip this scope, a `0` means the current object. - int scope_number[] = { - [kCdScopeWindow] = 0, // Number of window to look at. - [kCdScopeTabpage] = 0, // Number of tab to look at. - }; - - char *cwd = NULL; // Current working directory to print - char *from = NULL; // The original string to copy - - tabpage_T *tp = curtab; // The tabpage to look at. - win_T *win = curwin; // The window to look at. - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - // Pre-conditions and scope extraction together - for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) { - // If there is no argument there are no more scopes after it, break out. - if (argvars[i].v_type == VAR_UNKNOWN) { - break; - } - if (argvars[i].v_type != VAR_NUMBER) { - emsg(_(e_invarg)); - return; - } - scope_number[i] = (int)argvars[i].vval.v_number; - // It is an error for the scope number to be less than `-1`. - if (scope_number[i] < -1) { - emsg(_(e_invarg)); - return; - } - // Use the narrowest scope the user requested - if (scope_number[i] >= 0 && scope == kCdScopeInvalid) { - // The scope is the current iteration step. - scope = i; - } else if (scope_number[i] < 0) { - scope = i + 1; - } - } - - // Find the tabpage by number - if (scope_number[kCdScopeTabpage] > 0) { - tp = find_tabpage(scope_number[kCdScopeTabpage]); - if (!tp) { - emsg(_("E5000: Cannot find tab number.")); - return; - } - } - - // Find the window in `tp` by number, `NULL` if none. - if (scope_number[kCdScopeWindow] >= 0) { - if (scope_number[kCdScopeTabpage] < 0) { - emsg(_("E5001: Higher scope cannot be -1 if lower scope is >= 0.")); - return; - } - - if (scope_number[kCdScopeWindow] > 0) { - win = find_win_by_nr(&argvars[0], tp); - if (!win) { - emsg(_("E5002: Cannot find window number.")); - return; - } - } - } - - cwd = xmalloc(MAXPATHL); - - switch (scope) { - case kCdScopeWindow: - assert(win); - from = win->w_localdir; - if (from) { - break; - } - FALLTHROUGH; - case kCdScopeTabpage: - assert(tp); - from = tp->tp_localdir; - if (from) { - break; - } - FALLTHROUGH; - case kCdScopeGlobal: - if (globaldir) { // `globaldir` is not always set. - from = globaldir; - break; - } - FALLTHROUGH; // In global directory, just need to get OS CWD. - case kCdScopeInvalid: // If called without any arguments, get OS CWD. - if (os_dirname(cwd, MAXPATHL) == FAIL) { - from = ""; // Return empty string on failure. - } - } - - if (from) { - xstrlcpy(cwd, from, MAXPATHL); - } - - rettv->vval.v_string = xstrdup(cwd); -#ifdef BACKSLASH_IN_FILENAME - slash_adjust(rettv->vval.v_string); -#endif - - xfree(cwd); -} - /// "getfontname()" function static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -2644,98 +2505,6 @@ static void f_getfontname(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) rettv->vval.v_string = NULL; } -/// "getfperm({fname})" function -static void f_getfperm(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - char *perm = NULL; - char flags[] = "rwx"; - - const char *filename = tv_get_string(&argvars[0]); - int32_t file_perm = os_getperm(filename); - if (file_perm >= 0) { - perm = xstrdup("---------"); - for (int i = 0; i < 9; i++) { - if (file_perm & (1 << (8 - i))) { - perm[i] = flags[i % 3]; - } - } - } - rettv->v_type = VAR_STRING; - rettv->vval.v_string = perm; -} - -/// "getfsize({fname})" function -static void f_getfsize(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - const char *fname = tv_get_string(&argvars[0]); - - rettv->v_type = VAR_NUMBER; - - FileInfo file_info; - if (os_fileinfo(fname, &file_info)) { - uint64_t filesize = os_fileinfo_size(&file_info); - if (os_isdir(fname)) { - rettv->vval.v_number = 0; - } else { - rettv->vval.v_number = (varnumber_T)filesize; - - // non-perfect check for overflow - if ((uint64_t)rettv->vval.v_number != filesize) { - rettv->vval.v_number = -2; - } - } - } else { - rettv->vval.v_number = -1; - } -} - -/// "getftime({fname})" function -static void f_getftime(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - const char *fname = tv_get_string(&argvars[0]); - - FileInfo file_info; - if (os_fileinfo(fname, &file_info)) { - rettv->vval.v_number = (varnumber_T)file_info.stat.st_mtim.tv_sec; - } else { - rettv->vval.v_number = -1; - } -} - -/// "getftype({fname})" function -static void f_getftype(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - char *type = NULL; - char *t; - - const char *fname = tv_get_string(&argvars[0]); - - rettv->v_type = VAR_STRING; - FileInfo file_info; - if (os_fileinfo_link(fname, &file_info)) { - uint64_t mode = file_info.stat.st_mode; - if (S_ISREG(mode)) { - t = "file"; - } else if (S_ISDIR(mode)) { - t = "dir"; - } else if (S_ISLNK(mode)) { - t = "link"; - } else if (S_ISBLK(mode)) { - t = "bdev"; - } else if (S_ISCHR(mode)) { - t = "cdev"; - } else if (S_ISFIFO(mode)) { - t = "fifo"; - } else if (S_ISSOCK(mode)) { - t = "socket"; - } else { - t = "other"; - } - type = xstrdup(t); - } - rettv->vval.v_string = type; -} - /// "getjumplist()" function static void f_getjumplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -2824,7 +2593,7 @@ static char *block_def2str(struct block_def *bd) } static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2, - bool *const inclusive, MotionType *region_type, oparg_T *oa) + bool *const inclusive, MotionType *region_type, oparg_T *oap) FUNC_ATTR_NONNULL_ALL { tv_list_alloc_ret(rettv, kListLenMayKnow); @@ -2858,11 +2627,17 @@ static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2 type = default_type; } + int block_width = 0; 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) { + } else if (type[0] == Ctrl_V) { + char *p = type + 1; + if (*p != NUL && ((block_width = getdigits_int(&p, false, 0)) <= 0 || *p != NUL)) { + semsg(_(e_invargNval), "type", type); + return FAIL; + } *region_type = kMTBlockWise; } else { semsg(_(e_invargNval), "type", type); @@ -2926,16 +2701,18 @@ static int getregionpos(typval_T *argvars, typval_T *rettv, pos_T *p1, pos_T *p2 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; + oap->motion_type = kMTBlockWise; + oap->inclusive = true; + oap->op_type = OP_NOP; + oap->start = *p1; + oap->end = *p2; + oap->start_vcol = MIN(sc1, sc2); + if (block_width > 0) { + oap->end_vcol = oap->start_vcol + block_width - 1; + } else if (is_select_exclusive && ec1 < sc2 && 0 < sc2 && ec2 > ec1) { + oap->end_vcol = sc2 - 1; } else { - oa->end_vcol = MAX(ec1, ec2); + oap->end_vcol = MAX(ec1, ec2); } } @@ -3034,6 +2811,7 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr for (linenr_T lnum = p1.lnum; lnum <= p2.lnum; lnum++) { pos_T ret_p1, ret_p2; + char *line = ml_get(lnum); colnr_T line_len = ml_get_len(lnum); if (region_type == kMTLineWise) { @@ -3052,7 +2830,7 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr if (bd.is_oneChar) { // selection entirely inside one char if (region_type == kMTBlockWise) { - ret_p1.col = bd.textcol; + ret_p1.col = (colnr_T)(mb_prevptr(line, bd.textstart) - line) + 1; ret_p1.coladd = bd.start_char_vcols - (bd.start_vcol - oa.start_vcol); } else { ret_p1.col = p1.col + 1; @@ -3064,7 +2842,7 @@ static void f_getregionpos(typval_T *argvars, typval_T *rettv, EvalFuncData fptr ret_p1.coladd = oa.start_vcol - bd.start_vcol; bd.is_oneChar = true; } else if (bd.startspaces > 0) { - ret_p1.col = bd.textcol; + ret_p1.col = (colnr_T)(mb_prevptr(line, bd.textstart) - line) + 1; ret_p1.coladd = bd.start_char_vcols - bd.startspaces; } else { ret_p1.col = bd.textcol + 1; @@ -3269,113 +3047,6 @@ static void f_wait(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) time_watcher_close(tw, dummy_timer_close_cb); } -/// "glob()" function -static void f_glob(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - int options = WILD_SILENT|WILD_USE_NL; - expand_T xpc; - bool error = false; - - // When the optional second argument is non-zero, don't remove matches - // for 'wildignore' and don't put matches for 'suffixes' at the end. - rettv->v_type = VAR_STRING; - if (argvars[1].v_type != VAR_UNKNOWN) { - if (tv_get_number_chk(&argvars[1], &error)) { - options |= WILD_KEEP_ALL; - } - if (argvars[2].v_type != VAR_UNKNOWN) { - if (tv_get_number_chk(&argvars[2], &error)) { - tv_list_set_ret(rettv, NULL); - } - if (argvars[3].v_type != VAR_UNKNOWN - && tv_get_number_chk(&argvars[3], &error)) { - options |= WILD_ALLLINKS; - } - } - } - if (!error) { - ExpandInit(&xpc); - xpc.xp_context = EXPAND_FILES; - if (p_wic) { - options += WILD_ICASE; - } - if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ExpandOne(&xpc, (char *) - tv_get_string(&argvars[0]), NULL, options, - WILD_ALL); - } else { - ExpandOne(&xpc, (char *)tv_get_string(&argvars[0]), NULL, options, - WILD_ALL_KEEP); - tv_list_alloc_ret(rettv, xpc.xp_numfiles); - for (int i = 0; i < xpc.xp_numfiles; i++) { - tv_list_append_string(rettv->vval.v_list, xpc.xp_files[i], -1); - } - ExpandCleanup(&xpc); - } - } else { - rettv->vval.v_string = NULL; - } -} - -/// "globpath()" function -static void f_globpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - int flags = WILD_IGNORE_COMPLETESLASH; // Flags for globpath. - bool error = false; - - // Return a string, or a list if the optional third argument is non-zero. - rettv->v_type = VAR_STRING; - - if (argvars[2].v_type != VAR_UNKNOWN) { - // When the optional second argument is non-zero, don't remove matches - // for 'wildignore' and don't put matches for 'suffixes' at the end. - if (tv_get_number_chk(&argvars[2], &error)) { - flags |= WILD_KEEP_ALL; - } - - if (argvars[3].v_type != VAR_UNKNOWN) { - if (tv_get_number_chk(&argvars[3], &error)) { - tv_list_set_ret(rettv, NULL); - } - if (argvars[4].v_type != VAR_UNKNOWN - && tv_get_number_chk(&argvars[4], &error)) { - flags |= WILD_ALLLINKS; - } - } - } - - char buf1[NUMBUFLEN]; - const char *const file = tv_get_string_buf_chk(&argvars[1], buf1); - if (file != NULL && !error) { - garray_T ga; - ga_init(&ga, (int)sizeof(char *), 10); - globpath((char *)tv_get_string(&argvars[0]), (char *)file, &ga, flags, false); - - if (rettv->v_type == VAR_STRING) { - rettv->vval.v_string = ga_concat_strings_sep(&ga, "\n"); - } else { - tv_list_alloc_ret(rettv, ga.ga_len); - for (int i = 0; i < ga.ga_len; i++) { - tv_list_append_string(rettv->vval.v_list, - ((const char **)(ga.ga_data))[i], -1); - } - } - - ga_clear_strings(&ga); - } else { - rettv->vval.v_string = NULL; - } -} - -/// "glob2regpat()" function -static void f_glob2regpat(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - const char *const pat = tv_get_string_chk(&argvars[0]); // NULL on type error - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = (pat == NULL) ? NULL : file_pat_to_reg_pat(pat, NULL, NULL, false); -} - /// "gettext()" function static void f_gettext(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -3596,106 +3267,6 @@ static bool has_wsl(void) return has_wsl == kTrue; } -/// `haslocaldir([{win}[, {tab}]])` function -/// -/// Returns `1` if the scope object has a local directory, `0` otherwise. If a -/// scope object is not specified the current one is implied. This function -/// share a lot of code with `f_getcwd`. -/// -/// @pre The arguments must be of type number. -/// @pre There may not be more than two arguments. -/// @pre An argument may not be -1 if preceding arguments are not all -1. -/// -/// @post The return value will be either the number `1` or `0`. -static void f_haslocaldir(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) -{ - // Possible scope of working directory to return. - CdScope scope = kCdScopeInvalid; - - // Numbers of the scope objects (window, tab) we want the working directory - // of. A `-1` means to skip this scope, a `0` means the current object. - int scope_number[] = { - [kCdScopeWindow] = 0, // Number of window to look at. - [kCdScopeTabpage] = 0, // Number of tab to look at. - }; - - tabpage_T *tp = curtab; // The tabpage to look at. - win_T *win = curwin; // The window to look at. - - rettv->v_type = VAR_NUMBER; - rettv->vval.v_number = 0; - - // Pre-conditions and scope extraction together - for (int i = MIN_CD_SCOPE; i < MAX_CD_SCOPE; i++) { - if (argvars[i].v_type == VAR_UNKNOWN) { - break; - } - if (argvars[i].v_type != VAR_NUMBER) { - emsg(_(e_invarg)); - return; - } - scope_number[i] = (int)argvars[i].vval.v_number; - if (scope_number[i] < -1) { - emsg(_(e_invarg)); - return; - } - // Use the narrowest scope the user requested - if (scope_number[i] >= 0 && scope == kCdScopeInvalid) { - // The scope is the current iteration step. - scope = i; - } else if (scope_number[i] < 0) { - scope = i + 1; - } - } - - // If the user didn't specify anything, default to window scope - if (scope == kCdScopeInvalid) { - scope = MIN_CD_SCOPE; - } - - // Find the tabpage by number - if (scope_number[kCdScopeTabpage] > 0) { - tp = find_tabpage(scope_number[kCdScopeTabpage]); - if (!tp) { - emsg(_("E5000: Canno |