aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/eval')
-rw-r--r--src/nvim/eval/decode.c12
-rw-r--r--src/nvim/eval/encode.c7
-rw-r--r--src/nvim/eval/encode.h1
-rw-r--r--src/nvim/eval/executor.c4
-rw-r--r--src/nvim/eval/funcs.c60
-rw-r--r--src/nvim/eval/typval.c55
-rw-r--r--src/nvim/eval/typval.h24
-rw-r--r--src/nvim/eval/typval_encode.c.h17
-rw-r--r--src/nvim/eval/userfunc.c51
9 files changed, 172 insertions, 59 deletions
diff --git a/src/nvim/eval/decode.c b/src/nvim/eval/decode.c
index 42999ddd62..daba304f00 100644
--- a/src/nvim/eval/decode.c
+++ b/src/nvim/eval/decode.c
@@ -795,9 +795,9 @@ json_decode_string_cycle_start:
}
p += 3;
POP(((typval_T) {
- .v_type = VAR_SPECIAL,
+ .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED,
- .vval = { .v_special = kSpecialVarTrue },
+ .vval = { .v_bool = kBoolVarTrue },
}), false);
break;
}
@@ -808,9 +808,9 @@ json_decode_string_cycle_start:
}
p += 4;
POP(((typval_T) {
- .v_type = VAR_SPECIAL,
+ .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED,
- .vval = { .v_special = kSpecialVarFalse },
+ .vval = { .v_bool = kBoolVarFalse },
}), false);
break;
}
@@ -954,10 +954,10 @@ int msgpack_to_vim(const msgpack_object mobj, typval_T *const rettv)
}
case MSGPACK_OBJECT_BOOLEAN: {
*rettv = (typval_T) {
- .v_type = VAR_SPECIAL,
+ .v_type = VAR_BOOL,
.v_lock = VAR_UNLOCKED,
.vval = {
- .v_special = mobj.via.boolean ? kSpecialVarTrue : kSpecialVarFalse
+ .v_bool = mobj.via.boolean ? kBoolVarTrue : kBoolVarFalse
},
};
break;
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
index 138f638eb2..137f099df6 100644
--- a/src/nvim/eval/encode.c
+++ b/src/nvim/eval/encode.c
@@ -34,10 +34,13 @@
#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
#define utf_char2len(b) ((size_t)utf_char2len(b))
+const char *const encode_bool_var_names[] = {
+ [kBoolVarTrue] = "true",
+ [kBoolVarFalse] = "false",
+};
+
const char *const encode_special_var_names[] = {
[kSpecialVarNull] = "null",
- [kSpecialVarTrue] = "true",
- [kSpecialVarFalse] = "false",
};
#ifdef INCLUDE_GENERATED_DECLARATIONS
diff --git a/src/nvim/eval/encode.h b/src/nvim/eval/encode.h
index ccea245ab3..596bb49ae0 100644
--- a/src/nvim/eval/encode.h
+++ b/src/nvim/eval/encode.h
@@ -55,6 +55,7 @@ static inline ListReaderState encode_init_lrstate(const list_T *const list)
}
/// Array mapping values from SpecialVarValue enum to names
+extern const char *const encode_bool_var_names[];
extern const char *const encode_special_var_names[];
/// First codepoint in high surrogates block
diff --git a/src/nvim/eval/executor.c b/src/nvim/eval/executor.c
index 8cd21f8d62..da05ecda43 100644
--- a/src/nvim/eval/executor.c
+++ b/src/nvim/eval/executor.c
@@ -28,11 +28,13 @@ int eexe_mod_op(typval_T *const tv1, const typval_T *const tv2,
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NO_SANITIZE_UNDEFINED
{
// Can't do anything with a Funcref, a Dict or special value on the right.
- if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT) {
+ if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
+ && tv2->v_type != VAR_BOOL && tv2->v_type != VAR_SPECIAL) {
switch (tv1->v_type) {
case VAR_DICT:
case VAR_FUNC:
case VAR_PARTIAL:
+ case VAR_BOOL:
case VAR_SPECIAL: {
break;
}
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 25beb4be50..831167a489 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -175,8 +175,8 @@ static int non_zero_arg(typval_T *argvars)
{
return ((argvars[0].v_type == VAR_NUMBER
&& argvars[0].vval.v_number != 0)
- || (argvars[0].v_type == VAR_SPECIAL
- && argvars[0].vval.v_special == kSpecialVarTrue)
+ || (argvars[0].v_type == VAR_BOOL
+ && argvars[0].vval.v_bool == kBoolVarTrue)
|| (argvars[0].v_type == VAR_STRING
&& argvars[0].vval.v_string != NULL
&& *argvars[0].vval.v_string != NUL));
@@ -415,7 +415,7 @@ static void f_assert_equal(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rettv->vval.v_number = assert_equal_common(argvars, ASSERT_EQUAL);
}
-// "assert_equalfile(fname-one, fname-two)" function
+// "assert_equalfile(fname-one, fname-two[, msg])" function
static void f_assert_equalfile(typval_T *argvars, typval_T *rettv, FunPtr fptr)
{
rettv->vval.v_number = assert_equalfile(argvars);
@@ -824,9 +824,12 @@ static void f_call(typval_T *argvars, typval_T *rettv, FunPtr fptr)
} else if (argvars[0].v_type == VAR_PARTIAL) {
partial = argvars[0].vval.v_partial;
func = partial_name(partial);
+ } else if (nlua_is_table_from_lua(&argvars[0])) {
+ func = nlua_register_table_as_callable(&argvars[0]);
} else {
func = (char_u *)tv_get_string(&argvars[0]);
}
+
if (*func == NUL) {
return; // type error or empty name
}
@@ -1758,21 +1761,23 @@ static void f_empty(typval_T *argvars, typval_T *rettv, FunPtr fptr)
n = (tv_dict_len(argvars[0].vval.v_dict) == 0);
break;
}
- case VAR_SPECIAL: {
- // Using switch to get warning if SpecialVarValue receives more values.
- switch (argvars[0].vval.v_special) {
- case kSpecialVarTrue: {
+ case VAR_BOOL: {
+ switch (argvars[0].vval.v_bool) {
+ case kBoolVarTrue: {
n = false;
break;
}
- case kSpecialVarFalse:
- case kSpecialVarNull: {
+ case kBoolVarFalse: {
n = true;
break;
}
}
break;
}
+ case VAR_SPECIAL: {
+ n = argvars[0].vval.v_special == kSpecialVarNull;
+ break;
+ }
case VAR_UNKNOWN: {
internal_error("f_empty(UNKNOWN)");
break;
@@ -4860,6 +4865,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
bool rpc = false;
bool pty = false;
bool clear_env = false;
+ bool overlapped = false;
CallbackReader on_stdout = CALLBACK_READER_INIT,
on_stderr = CALLBACK_READER_INIT;
Callback on_exit = CALLBACK_NONE;
@@ -4871,12 +4877,23 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
rpc = tv_dict_get_number(job_opts, "rpc") != 0;
pty = tv_dict_get_number(job_opts, "pty") != 0;
clear_env = tv_dict_get_number(job_opts, "clear_env") != 0;
+ overlapped = tv_dict_get_number(job_opts, "overlapped") != 0;
+
if (pty && rpc) {
EMSG2(_(e_invarg2), "job cannot have both 'pty' and 'rpc' options set");
shell_free_argv(argv);
return;
}
+#ifdef WIN32
+ if (pty && overlapped) {
+ EMSG2(_(e_invarg2),
+ "job cannot have both 'pty' and 'overlapped' options set");
+ shell_free_argv(argv);
+ return;
+ }
+#endif
+
char *new_cwd = tv_dict_get_string(job_opts, "cwd", false);
if (new_cwd && strlen(new_cwd) > 0) {
cwd = new_cwd;
@@ -4943,7 +4960,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
}
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit, pty,
- rpc, detach, cwd, width, height,
+ rpc, overlapped, detach, cwd, width, height,
term_name, env, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
@@ -5189,6 +5206,7 @@ static void f_len(typval_T *argvars, typval_T *rettv, FunPtr fptr)
break;
}
case VAR_UNKNOWN:
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_FLOAT:
case VAR_PARTIAL:
@@ -7369,8 +7387,8 @@ static void f_rpcstart(typval_T *argvars, typval_T *rettv, FunPtr fptr)
Channel *chan = channel_job_start(argv, CALLBACK_READER_INIT,
CALLBACK_READER_INIT, CALLBACK_NONE,
- false, true, false, NULL, 0, 0, NULL, NULL,
- &rettv->vval.v_number);
+ false, true, false, false, NULL, 0, 0,
+ NULL, NULL, &rettv->vval.v_number);
if (chan) {
channel_create_event(chan, NULL);
}
@@ -10458,7 +10476,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr)
uint16_t term_width = MAX(0, curwin->w_width_inner - win_col_off(curwin));
Channel *chan = channel_job_start(argv, on_stdout, on_stderr, on_exit,
- true, false, false, cwd,
+ true, false, false, false, cwd,
term_width, curwin->w_height_inner,
xstrdup("xterm-256color"), NULL,
&rettv->vval.v_number);
@@ -10808,20 +10826,8 @@ static void f_type(typval_T *argvars, typval_T *rettv, FunPtr fptr)
case VAR_LIST: n = VAR_TYPE_LIST; break;
case VAR_DICT: n = VAR_TYPE_DICT; break;
case VAR_FLOAT: n = VAR_TYPE_FLOAT; break;
- case VAR_SPECIAL: {
- switch (argvars[0].vval.v_special) {
- case kSpecialVarTrue:
- case kSpecialVarFalse: {
- n = VAR_TYPE_BOOL;
- break;
- }
- case kSpecialVarNull: {
- n = 7;
- break;
- }
- }
- break;
- }
+ case VAR_BOOL: n = VAR_TYPE_BOOL; break;
+ case VAR_SPECIAL:n = VAR_TYPE_SPECIAL; break;
case VAR_UNKNOWN: {
internal_error("f_type(UNKNOWN)");
break;
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
index 35130f6f40..2394eb8099 100644
--- a/src/nvim/eval/typval.c
+++ b/src/nvim/eval/typval.c
@@ -16,6 +16,7 @@
#include "nvim/eval/typval_encode.h"
#include "nvim/eval.h"
#include "nvim/eval/userfunc.h"
+#include "nvim/lua/executor.h"
#include "nvim/types.h"
#include "nvim/assert.h"
#include "nvim/memory.h"
@@ -301,6 +302,7 @@ void tv_list_free_list(list_T *const l)
}
list_log(l, NULL, NULL, "freelist");
+ nlua_free_typval_list(l);
xfree(l);
}
@@ -797,10 +799,14 @@ bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic,
if (l1 == l2) {
return true;
}
- if (l1 == NULL || l2 == NULL) {
+ if (tv_list_len(l1) != tv_list_len(l2)) {
return false;
}
- if (tv_list_len(l1) != tv_list_len(l2)) {
+ if (tv_list_len(l1) == 0) {
+ // empty and NULL list are considered equal
+ return true;
+ }
+ if (l1 == NULL || l2 == NULL) {
return false;
}
@@ -1374,6 +1380,7 @@ void tv_dict_free_dict(dict_T *const d)
d->dv_used_next->dv_used_prev = d->dv_used_prev;
}
+ nlua_free_typval_dict(d);
xfree(d);
}
@@ -1693,21 +1700,21 @@ int tv_dict_add_float(dict_T *const d, const char *const key,
return OK;
}
-/// Add a special entry to dictionary
+/// Add a boolean entry to dictionary
///
/// @param[out] d Dictionary to add entry to.
/// @param[in] key Key to add.
/// @param[in] key_len Key length.
-/// @param[in] val SpecialVarValue to add.
+/// @param[in] val BoolVarValue to add.
///
/// @return OK in case of success, FAIL when key already exists.
-int tv_dict_add_special(dict_T *const d, const char *const key,
- const size_t key_len, SpecialVarValue val)
+int tv_dict_add_bool(dict_T *const d, const char *const key,
+ const size_t key_len, BoolVarValue val)
{
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
- item->di_tv.v_type = VAR_SPECIAL;
- item->di_tv.vval.v_special = val;
+ item->di_tv.v_type = VAR_BOOL;
+ item->di_tv.vval.v_bool = val;
if (tv_dict_add(d, item) == FAIL) {
tv_dict_item_free(item);
return FAIL;
@@ -2013,12 +2020,15 @@ void tv_dict_alloc_ret(typval_T *const ret_tv)
#define TYPVAL_ENCODE_CONV_NIL(tv) \
do { \
- tv->vval.v_special = kSpecialVarFalse; \
+ tv->vval.v_special = kSpecialVarNull; \
tv->v_lock = VAR_UNLOCKED; \
} while (0)
#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
- TYPVAL_ENCODE_CONV_NIL(tv)
+ do { \
+ tv->vval.v_bool = kBoolVarFalse; \
+ tv->v_lock = VAR_UNLOCKED; \
+ } while (0)
#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
do { \
@@ -2293,6 +2303,7 @@ void tv_free(typval_T *tv)
tv_dict_unref(tv->vval.v_dict);
break;
}
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_NUMBER:
case VAR_FLOAT:
@@ -2324,6 +2335,7 @@ void tv_copy(const typval_T *const from, typval_T *const to)
switch (from->v_type) {
case VAR_NUMBER:
case VAR_FLOAT:
+ case VAR_BOOL:
case VAR_SPECIAL: {
break;
}
@@ -2425,6 +2437,7 @@ void tv_item_lock(typval_T *const tv, const int deep, const bool lock)
case VAR_STRING:
case VAR_FUNC:
case VAR_PARTIAL:
+ case VAR_BOOL:
case VAR_SPECIAL: {
break;
}
@@ -2588,6 +2601,9 @@ bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic,
const char *s2 = tv_get_string_buf(tv2, buf2);
return mb_strcmp_ic((bool)ic, s1, s2) == 0;
}
+ case VAR_BOOL: {
+ return tv1->vval.v_bool == tv2->vval.v_bool;
+ }
case VAR_SPECIAL: {
return tv1->vval.v_special == tv2->vval.v_special;
}
@@ -2638,6 +2654,10 @@ bool tv_check_str_or_nr(const typval_T *const tv)
EMSG(_("E728: Expected a Number or a String, Dictionary found"));
return false;
}
+ case VAR_BOOL: {
+ EMSG(_("E5299: Expected a Number or a String, Boolean found"));
+ return false;
+ }
case VAR_SPECIAL: {
EMSG(_("E5300: Expected a Number or a String"));
return false;
@@ -2677,6 +2697,7 @@ bool tv_check_num(const typval_T *const tv)
{
switch (tv->v_type) {
case VAR_NUMBER:
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_STRING: {
return true;
@@ -2721,6 +2742,7 @@ bool tv_check_str(const typval_T *const tv)
{
switch (tv->v_type) {
case VAR_NUMBER:
+ case VAR_BOOL:
case VAR_SPECIAL:
case VAR_STRING: {
return true;
@@ -2791,8 +2813,11 @@ varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error)
}
return n;
}
+ case VAR_BOOL: {
+ return tv->vval.v_bool == kBoolVarTrue ? 1 : 0;
+ }
case VAR_SPECIAL: {
- return tv->vval.v_special == kSpecialVarTrue ? 1 : 0;
+ return 0;
}
case VAR_UNKNOWN: {
emsgf(_(e_intern2), "tv_get_number(UNKNOWN)");
@@ -2860,6 +2885,10 @@ float_T tv_get_float(const typval_T *const tv)
EMSG(_("E894: Using a Dictionary as a Float"));
break;
}
+ case VAR_BOOL: {
+ EMSG(_("E362: Using a boolean value as a Float"));
+ break;
+ }
case VAR_SPECIAL: {
EMSG(_("E907: Using a special value as a Float"));
break;
@@ -2897,6 +2926,10 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf)
}
return "";
}
+ case VAR_BOOL: {
+ STRCPY(buf, encode_bool_var_names[tv->vval.v_bool]);
+ return buf;
+ }
case VAR_SPECIAL: {
STRCPY(buf, encode_special_var_names[tv->vval.v_special]);
return buf;
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index 4390db1b71..503a32a81e 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -91,10 +91,14 @@ typedef struct dict_watcher {
bool busy; // prevent recursion if the dict is changed in the callback
} DictWatcher;
+/// Bool variable values
+typedef enum {
+ kBoolVarFalse, ///< v:false
+ kBoolVarTrue, ///< v:true
+} BoolVarValue;
+
/// Special variable values
typedef enum {
- kSpecialVarFalse, ///< v:false
- kSpecialVarTrue, ///< v:true
kSpecialVarNull, ///< v:null
} SpecialVarValue;
@@ -114,6 +118,7 @@ typedef enum {
VAR_LIST, ///< List, .v_list is used.
VAR_DICT, ///< Dictionary, .v_dict is used.
VAR_FLOAT, ///< Floating-point value, .v_float is used.
+ VAR_BOOL, ///< true, false
VAR_SPECIAL, ///< Special value (true, false, null), .v_special
///< is used.
VAR_PARTIAL, ///< Partial, .v_partial is used.
@@ -125,6 +130,7 @@ typedef struct {
VarLockStatus v_lock; ///< Variable lock status.
union typval_vval_union {
varnumber_T v_number; ///< Number, for VAR_NUMBER.
+ BoolVarValue v_bool; ///< Bool value, for VAR_BOOL
SpecialVarValue v_special; ///< Special value, for VAR_SPECIAL.
float_T v_float; ///< Floating-point number, for VAR_FLOAT.
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
@@ -174,6 +180,8 @@ struct listvar_S {
int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
int lv_copyID; ///< ID used by deepcopy().
VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
+
+ LuaRef lua_table_ref;
};
// Static list with 10 items. Use tv_list_init_static10() to initialize.
@@ -239,6 +247,8 @@ struct dictvar_S {
dict_T *dv_used_next; ///< Next dictionary in used dictionaries list.
dict_T *dv_used_prev; ///< Previous dictionary in used dictionaries list.
QUEUE watchers; ///< Dictionary key watchers set by user code.
+
+ LuaRef lua_table_ref;
};
/// Type used for script ID
@@ -265,6 +275,12 @@ typedef struct {
/// Number of fixed variables used for arguments
#define FIXVAR_CNT 12
+/// Callback interface for C function reference>
+/// Used for managing functions that were registered with |register_cfunc|
+typedef int (*cfunc_T)(int argcount, typval_T *argvars, typval_T *rettv, void *state); // NOLINT
+/// Callback to clear cfunc_T and any associated state.
+typedef void (*cfunc_free_T)(void *state);
+
// Structure to hold info for a function that is currently being executed.
typedef struct funccall_S funccall_T;
@@ -301,6 +317,10 @@ struct ufunc {
garray_T uf_lines; ///< function lines
int uf_profiling; ///< true when func is being profiled
int uf_prof_initialized;
+ // Managing cfuncs
+ cfunc_T uf_cb; ///< C function extension callback
+ cfunc_free_T uf_cb_free; ///< C function extesion free callback
+ void *uf_cb_state; ///< State of C function extension.
// Profiling the function as a whole.
int uf_tm_count; ///< nr of calls
proftime_T uf_tm_total; ///< time spent in function + children
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
index 94986a64b5..91c948ce7e 100644
--- a/src/nvim/eval/typval_encode.c.h
+++ b/src/nvim/eval/typval_encode.c.h
@@ -364,7 +364,7 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
_TYPVAL_ENCODE_DO_CHECK_SELF_REFERENCE(tv->vval.v_list, lv_copyID, copyID,
kMPConvList);
TYPVAL_ENCODE_CONV_LIST_START(tv, tv_list_len(tv->vval.v_list));
- assert(saved_copyID != copyID && saved_copyID != copyID - 1);
+ assert(saved_copyID != copyID);
_mp_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvList,
.tv = tv,
@@ -379,17 +379,22 @@ static int _TYPVAL_ENCODE_CONVERT_ONE_VALUE(
TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, _mp_last(*mpstack));
break;
}
+ case VAR_BOOL: {
+ switch (tv->vval.v_bool) {
+ case kBoolVarTrue:
+ case kBoolVarFalse: {
+ TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_bool == kBoolVarTrue);
+ break;
+ }
+ }
+ break;
+ }
case VAR_SPECIAL: {
switch (tv->vval.v_special) {
case kSpecialVarNull: {
TYPVAL_ENCODE_CONV_NIL(tv); // -V1037
break;
}
- case kSpecialVarTrue:
- case kSpecialVarFalse: {
- TYPVAL_ENCODE_CONV_BOOL(tv, tv->vval.v_special == kSpecialVarTrue);
- break;
- }
}
break;
}
diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c
index 4d658498c1..229f0e8dde 100644
--- a/src/nvim/eval/userfunc.c
+++ b/src/nvim/eval/userfunc.c
@@ -32,6 +32,7 @@
#define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0
#define FC_REMOVED 0x20 // function redefined while uf_refcount > 0
#define FC_SANDBOX 0x40 // function defined in the sandbox
+#define FC_CFUNC 0x80 // C function extension
#ifdef INCLUDE_GENERATED_DECLARATIONS
#include "eval/userfunc.c.generated.h"
@@ -162,6 +163,17 @@ static void register_closure(ufunc_T *fp)
[current_funccal->fc_funcs.ga_len++] = fp;
}
+
+/// Get a name for a lambda. Returned in static memory.
+char_u * get_lambda_name(void)
+{
+ static char_u name[30];
+ static int lambda_no = 0;
+
+ snprintf((char *)name, sizeof(name), "<lambda>%d", ++lambda_no);
+ return name;
+}
+
/// Parse a lambda expression and get a Funcref from "*arg".
///
/// @return OK or FAIL. Returns NOTDONE for dict or {expr}.
@@ -175,7 +187,6 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
int ret;
char_u *start = skipwhite(*arg + 1);
char_u *s, *e;
- static int lambda_no = 0;
bool *old_eval_lavars = eval_lavars_used;
bool eval_lavars = false;
@@ -219,11 +230,9 @@ int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate)
if (evaluate) {
int len, flags = 0;
char_u *p;
- char_u name[20];
garray_T newlines;
- lambda_no++;
- snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no);
+ char_u *name = get_lambda_name();
fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
pt = xcalloc(1, sizeof(partial_T));
@@ -700,6 +709,11 @@ static void func_clear_items(ufunc_T *fp)
ga_clear_strings(&(fp->uf_args));
ga_clear_strings(&(fp->uf_lines));
+ if (fp->uf_cb_free != NULL) {
+ fp->uf_cb_free(fp->uf_cb_state);
+ fp->uf_cb_free = NULL;
+ }
+
XFREE_CLEAR(fp->uf_tml_count);
XFREE_CLEAR(fp->uf_tml_total);
XFREE_CLEAR(fp->uf_tml_self);
@@ -1408,6 +1422,9 @@ call_func(
if (fp != NULL && (fp->uf_flags & FC_DELETED)) {
error = ERROR_DELETED;
+ } else if (fp != NULL && (fp->uf_flags & FC_CFUNC)) {
+ cfunc_T cb = fp->uf_cb;
+ error = (*cb)(argcount, argvars, rettv, fp->uf_cb_state);
} else if (fp != NULL) {
if (argv_func != NULL) {
// postponed filling in the arguments, do it now
@@ -3435,3 +3452,29 @@ bool set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
xfree(tofree);
return abort;
}
+
+/// Registers a C extension user function.
+char_u *register_cfunc(cfunc_T cb, cfunc_free_T cb_free, void *state)
+{
+ char_u *name = get_lambda_name();
+ ufunc_T *fp = NULL;
+
+ fp = xcalloc(1, offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
+ if (fp == NULL) {
+ return NULL;
+ }
+
+ fp->uf_refcount = 1;
+ fp->uf_varargs = true;
+ fp->uf_flags = FC_CFUNC;
+ fp->uf_calls = 0;
+ fp->uf_script_ctx = current_sctx;
+ fp->uf_cb = cb;
+ fp->uf_cb_free = cb_free;
+ fp->uf_cb_state = state;
+
+ STRCPY(fp->uf_name, name);
+ hash_add(&func_hashtab, UF2HIKEY(fp));
+
+ return fp->uf_name;
+}