patch 9.1.0831: 'findexpr' can't be used as lambad or Funcref

Problem:  'findexpr' can't be used for lambads
          (Justin Keyes)
Solution: Replace the findexpr option with the findfunc option
          (Yegappan Lakshmanan)

related: #15905
closes: #15976

Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index d60e344..380bd23 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -6924,58 +6924,71 @@
 }
 
 #if defined(FEAT_EVAL) || defined(PROTO)
-/*
- * Evaluate the 'findexpr' expression and return the result.  When evaluating
- * the expression, v:fname is set to the ":find" command argument.
- */
-    static list_T *
-eval_findexpr(char_u *pat, int cmdcomplete)
+
+// callback function for 'findfunc'
+static callback_T ffu_cb;
+
+    static callback_T *
+get_findfunc_callback(void)
 {
+    return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb;
+}
+
+    static list_T *
+call_findfunc(char_u *pat, int cmdcomplete)
+{
+    typval_T	args[3];
+    callback_T	*cb;
+    typval_T	rettv;
+    int		retval;
     sctx_T	saved_sctx = current_sctx;
-    char_u	*findexpr;
-    char_u	*arg;
-    typval_T	tv;
-    list_T	*retlist = NULL;
+    sctx_T	*ctx;
 
-    findexpr = get_findexpr();
+    // Call 'findfunc' to obtain the list of file names.
+    args[0].v_type = VAR_STRING;
+    args[0].vval.v_string = pat;
+    args[1].v_type = VAR_BOOL;
+    args[1].vval.v_number = cmdcomplete;
+    args[2].v_type = VAR_UNKNOWN;
 
-    set_vim_var_string(VV_FNAME, pat, -1);
-    set_vim_var_nr(VV_CMDCOMPLETE, cmdcomplete ? VVAL_TRUE : VVAL_FALSE);
-    current_sctx = curbuf->b_p_script_ctx[BV_FEXPR];
-
-    arg = skipwhite(findexpr);
-
+    // Lock the text to prevent weird things from happening.  Also disallow
+    // switching to another window, it should not be needed and may end up in
+    // Insert mode in another buffer.
     ++textlock;
 
-    // Evaluate the expression.  If the expression is "FuncName()" call the
-    // function directly.
-    if (eval0_simple_funccal(arg, &tv, NULL, &EVALARG_EVALUATE) == FAIL)
-	retlist = NULL;
-    else
-    {
-	if (tv.v_type == VAR_LIST)
-	    retlist = list_copy(tv.vval.v_list, TRUE, TRUE, get_copyID());
-	else
-	    emsg(_(e_invalid_return_type_from_findexpr));
-	clear_tv(&tv);
-    }
-    --textlock;
-    clear_evalarg(&EVALARG_EVALUATE, NULL);
+    ctx = get_option_sctx("findfunc");
+    if (ctx != NULL)
+	current_sctx = *ctx;
 
-    set_vim_var_string(VV_FNAME, NULL, 0);
-    set_vim_var_nr(VV_CMDCOMPLETE, VVAL_FALSE);
+    cb = get_findfunc_callback();
+    retval = call_callback(cb, -1, &rettv, 2, args);
+
     current_sctx = saved_sctx;
 
+    --textlock;
+
+    list_T *retlist = NULL;
+
+    if (retval == OK)
+    {
+	if (rettv.v_type == VAR_LIST)
+	    retlist = list_copy(rettv.vval.v_list, FALSE, FALSE, get_copyID());
+	else
+	    emsg(_(e_invalid_return_type_from_findfunc));
+
+	clear_tv(&rettv);
+    }
+
     return retlist;
 }
 
 /*
- * Find file names matching "pat" using 'findexpr' and return it in "files".
+ * Find file names matching "pat" using 'findfunc' and return it in "files".
  * Used for expanding the :find, :sfind and :tabfind command argument.
  * Returns OK on success and FAIL otherwise.
  */
     int
-expand_findexpr(char_u *pat, char_u ***files, int *numMatches)
+expand_findfunc(char_u *pat, char_u ***files, int *numMatches)
 {
     list_T	*l;
     int		len;
@@ -6983,7 +6996,7 @@
     *numMatches = 0;
     *files = NULL;
 
-    l = eval_findexpr(pat, TRUE);
+    l = call_findfunc(pat, VVAL_TRUE);
 
     if (l == NULL)
 	return FAIL;
@@ -7015,11 +7028,11 @@
 }
 
 /*
- * Use 'findexpr' to find file 'findarg'.  The 'count' argument is used to find
+ * Use 'findfunc' to find file 'findarg'.  The 'count' argument is used to find
  * the n'th matching file.
  */
     static char_u *
-findexpr_find_file(char_u *findarg, int findarg_len, int count)
+findfunc_find_file(char_u *findarg, int findarg_len, int count)
 {
     list_T	*fname_list;
     char_u	*ret_fname = NULL;
@@ -7029,7 +7042,7 @@
     cc = findarg[findarg_len];
     findarg[findarg_len] = NUL;
 
-    fname_list = eval_findexpr(findarg, FALSE);
+    fname_list = call_findfunc(findarg, VVAL_FALSE);
     fname_count = list_len(fname_list);
 
     if (fname_count == 0)
@@ -7053,6 +7066,62 @@
 
     return ret_fname;
 }
+
+/*
+ * Process the 'findfunc' option value.
+ * Returns NULL on success and an error message on failure.
+ */
+    char *
+did_set_findfunc(optset_T *args UNUSED)
+{
+    int	retval;
+
+    if (*curbuf->b_p_ffu != NUL)
+    {
+	// buffer-local option set
+	retval = option_set_callback_func(curbuf->b_p_ffu, &curbuf->b_ffu_cb);
+    }
+    else
+    {
+	// global option set
+	retval = option_set_callback_func(p_ffu, &ffu_cb);
+    }
+
+    if (retval == FAIL)
+	return e_invalid_argument;
+
+    // If the option value starts with <SID> or s:, then replace that with
+    // the script identifier.
+    char_u	**varp = (char_u **)args->os_varp;
+    char_u	*name = get_scriptlocal_funcname(*varp);
+    if (name != NULL)
+    {
+	free_string_option(*varp);
+	*varp = name;
+    }
+
+    return NULL;
+}
+
+# if defined(EXITFREE) || defined(PROTO)
+    void
+free_findfunc_option(void)
+{
+    free_callback(&ffu_cb);
+}
+# endif
+
+/*
+ * Mark the global 'findfunc' callback with "copyID" so that it is not
+ * garbage collected.
+ */
+    int
+set_ref_in_findfunc(int copyID UNUSED)
+{
+    int abort = FALSE;
+    abort = set_ref_in_callback(&ffu_cb, copyID);
+    return abort;
+}
 #endif
 
 /*
@@ -7105,10 +7174,10 @@
 	char_u	*file_to_find = NULL;
 	char	*search_ctx = NULL;
 
-	if (*get_findexpr() != NUL)
+	if (*get_findfunc() != NUL)
 	{
 #ifdef FEAT_EVAL
-	    fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg),
+	    fname = findfunc_find_file(eap->arg, (int)STRLEN(eap->arg),
 				       eap->addr_count > 0 ? eap->line2 : 1);
 #endif
 	}
@@ -7389,10 +7458,10 @@
     char_u	*file_to_find = NULL;
     char	*search_ctx = NULL;
 
-    if (*get_findexpr() != NUL)
+    if (*get_findfunc() != NUL)
     {
 #ifdef FEAT_EVAL
-	fname = findexpr_find_file(eap->arg, (int)STRLEN(eap->arg),
+	fname = findfunc_find_file(eap->arg, (int)STRLEN(eap->arg),
 					eap->addr_count > 0 ? eap->line2 : 1);
 #endif
     }