aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/eval.c
diff options
context:
space:
mode:
authorMichael Ennen <mike.ennen@gmail.com>2016-12-17 16:01:42 -0700
committerMichael Ennen <mike.ennen@gmail.com>2017-02-14 17:38:17 -0700
commitf59321e319ecfc6977f666fb04a52ac50bead09d (patch)
tree9c98a259e9810ec6f2edc7d020d6164e64bb79e1 /src/nvim/eval.c
parent9f6f7fe26d7caa89083f5d5b00c55bf2046591ca (diff)
downloadrneovim-f59321e319ecfc6977f666fb04a52ac50bead09d.tar.gz
rneovim-f59321e319ecfc6977f666fb04a52ac50bead09d.tar.bz2
rneovim-f59321e319ecfc6977f666fb04a52ac50bead09d.zip
vim-patch:7.4.2120
Problem: User defined functions can't be a closure. Solution: Add the "closure" argument. Allow using :unlet on a bound variable. (Yasuhiro Matsumoto, Ken Takata) https://github.com/vim/vim/commit/10ce39a0d52272a3dfff2feb8c631529f29e6740
Diffstat (limited to 'src/nvim/eval.c')
-rw-r--r--src/nvim/eval.c90
1 files changed, 77 insertions, 13 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 63af474030..5ebd86a59d 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -209,10 +209,13 @@ static int echo_attr = 0; /* attributes used for ":echo" */
#define GLV_QUIET TFN_QUIET /* no error messages */
#define GLV_NO_AUTOLOAD TFN_NO_AUTOLOAD /* do not use script autoloading */
-/* function flags */
-#define FC_ABORT 1 /* abort function on error */
-#define FC_RANGE 2 /* function accepts range */
-#define FC_DICT 4 /* Dict function, uses "self" */
+// function flags
+#define FC_ABORT 1 // abort function on error
+#define FC_RANGE 2 // function accepts range
+#define FC_DICT 4 // Dict function, uses "self"
+#define FC_CLOSURE 8 // closure, uses outer scope variables
+#define FC_DELETED 16 // :delfunction used while uf_refcount > 0
+#define FC_REMOVED 32 // function redefined while uf_refcount > 0
/* The names of packages that once were loaded are remembered. */
static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
@@ -3070,7 +3073,10 @@ int do_unlet(char_u *name, int forceit)
return FAIL;
}
hi = hash_find(ht, varname);
- if (!HASHITEM_EMPTY(hi)) {
+ if (HASHITEM_EMPTY(hi)) {
+ hi = find_hi_in_scoped_ht(name, &varname, &ht);
+ }
+ if (hi != NULL && !HASHITEM_EMPTY(hi)) {
di = HI2DI(hi);
if (var_check_fixed(di->di_flags, name, false)
|| var_check_ro(di->di_flags, name, false)
@@ -7070,7 +7076,7 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
(*arg)++;
if (evaluate) {
- int len;
+ int len, flags = 0;
char_u *p;
snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
@@ -7100,6 +7106,7 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
fp->uf_args = newargs;
fp->uf_lines = newlines;
if (current_funccal != NULL && eval_lavars) {
+ flags |= FC_CLOSURE;
fp->uf_scoped = current_funccal;
current_funccal->fc_refcount++;
ga_grow(&current_funccal->fc_funcs, 1);
@@ -7118,7 +7125,7 @@ static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
func_do_profile(fp);
}
fp->uf_varargs = true;
- fp->uf_flags = 0;
+ fp->uf_flags = flags;
fp->uf_calls = 0;
fp->uf_script_ID = current_SID;
@@ -21050,7 +21057,7 @@ void ex_function(exarg_T *eap)
goto errret_2;
}
- // find extra arguments "range", "dict" and "abort"
+ // find extra arguments "range", "dict", "abort" and "closure"
for (;; ) {
p = skipwhite(p);
if (STRNCMP(p, "range", 5) == 0) {
@@ -21062,8 +21069,12 @@ void ex_function(exarg_T *eap)
} else if (STRNCMP(p, "abort", 5) == 0) {
flags |= FC_ABORT;
p += 5;
- } else
+ } else if (STRNCMP(p, "closure", 7) == 0) {
+ flags |= FC_CLOSURE;
+ p += 7;
+ } else {
break;
+ }
}
/* When there is a line break use what follows for the function body.
@@ -21346,7 +21357,21 @@ void ex_function(exarg_T *eap)
fp->uf_refcount = 1;
fp->uf_args = newargs;
fp->uf_lines = newlines;
- fp->uf_scoped = NULL;
+ if ((flags & FC_CLOSURE) != 0) {
+ if (current_funccal == NULL) {
+ emsg_funcname(N_("E932 Closure function should not be at top level: %s"),
+ name);
+ goto erret;
+ }
+ fp->uf_scoped = current_funccal;
+ current_funccal->fc_refcount++;
+ ga_grow(&current_funccal->fc_funcs, 1);
+ ((ufunc_T **)current_funccal->fc_funcs.ga_data)
+ [current_funccal->fc_funcs.ga_len++] = fp;
+ func_ref(current_funccal->func->uf_name);
+ } else {
+ fp->uf_scoped = NULL;
+ }
fp->uf_tml_count = NULL;
fp->uf_tml_total = NULL;
fp->uf_tml_self = NULL;
@@ -21627,12 +21652,18 @@ static void list_func_head(ufunc_T *fp, int indent)
MSG_PUTS("...");
}
msg_putchar(')');
- if (fp->uf_flags & FC_ABORT)
+ if (fp->uf_flags & FC_ABORT) {
MSG_PUTS(" abort");
- if (fp->uf_flags & FC_RANGE)
+ }
+ if (fp->uf_flags & FC_RANGE) {
MSG_PUTS(" range");
- if (fp->uf_flags & FC_DICT)
+ }
+ if (fp->uf_flags & FC_DICT) {
MSG_PUTS(" dict");
+ }
+ if (fp->uf_flags & FC_CLOSURE) {
+ MSG_PUTS(" closure");
+ }
msg_clr_eos();
if (p_verbose > 0)
last_set_msg(fp->uf_script_ID);
@@ -22954,6 +22985,39 @@ static var_flavour_T var_flavour(char_u *varname)
}
}
+/// Search hashitem in parent scope.
+hashitem_T *find_hi_in_scoped_ht(char_u *name, char_u **varname,
+ hashtab_T **pht)
+{
+ funccall_T *old_current_funccal = current_funccal;
+ hashtab_T *ht;
+ hashitem_T *hi = NULL;
+
+ if (current_funccal == NULL || current_funccal->func->uf_scoped == NULL) {
+ return NULL;
+ }
+
+ // Search in parent scope which is possible to reference from lambda
+ current_funccal = current_funccal->func->uf_scoped;
+ while (current_funccal) {
+ ht = find_var_ht(name, varname);
+ if (ht != NULL && **varname != NUL) {
+ hi = hash_find(ht, *varname);
+ if (!HASHITEM_EMPTY(hi)) {
+ *pht = ht;
+ break;
+ }
+ }
+ if (current_funccal == current_funccal->func->uf_scoped) {
+ break;
+ }
+ current_funccal = current_funccal->func->uf_scoped;
+ }
+ current_funccal = old_current_funccal;
+
+ return hi;
+}
+
/// Search variable in parent scope.
dictitem_T *find_var_in_scoped_ht(char_u *name, char_u **varname,
int no_autoload)