aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval/funcs.c
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2024-11-19 22:57:13 +0000
committerJosh Rahm <joshuarahm@gmail.com>2024-11-19 22:57:13 +0000
commit9be89f131f87608f224f0ee06d199fcd09d32176 (patch)
tree11022dcfa9e08cb4ac5581b16734196128688d48 /src/nvim/eval/funcs.c
parentff7ed8f586589d620a806c3758fac4a47a8e7e15 (diff)
parent88085c2e80a7e3ac29aabb6b5420377eed99b8b6 (diff)
downloadrneovim-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.c1811
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