mirror of
				https://github.com/neovim/neovim.git
				synced 2025-11-04 01:34:25 +00:00 
			
		
		
		
	vim-patch:7.4.2044
Problem:    filter() and map() either require a string or defining a function.
Solution:   Support lambda, a short way to define a function that evaluates an
            expression. (Yasuhiro Matsumoto, Ken Takata)
069c1e7fa9
			
			
This commit is contained in:
		@@ -112,9 +112,10 @@ You will not get an error if you try to change the type of a variable.
 | 
			
		||||
	
 | 
			
		||||
1.2 Function references ~
 | 
			
		||||
					*Funcref* *E695* *E718*
 | 
			
		||||
A Funcref variable is obtained with the |function()| function.	It can be used
 | 
			
		||||
in an expression in the place of a function name, before the parenthesis
 | 
			
		||||
around the arguments, to invoke the function it refers to.  Example: >
 | 
			
		||||
A Funcref variable is obtained with the |function()| function or created with
 | 
			
		||||
the lambda expression |expr-lambda|. It can be used in an expression in the
 | 
			
		||||
place of a function name, before the parenthesis around the arguments, to
 | 
			
		||||
invoke the function it refers to.  Example: >
 | 
			
		||||
 | 
			
		||||
	:let Fn = function("MyFunc")
 | 
			
		||||
	:echo Fn()
 | 
			
		||||
@@ -667,6 +668,7 @@ Expression syntax summary, from least to most significant:
 | 
			
		||||
	@r			contents of register 'r'
 | 
			
		||||
	function(expr1, ...)	function call
 | 
			
		||||
	func{ti}on(expr1, ...)	function call with curly braces
 | 
			
		||||
	{args -> expr1}		lambda expression
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
".." indicates that the operations in this level can be concatenated.
 | 
			
		||||
@@ -1174,6 +1176,42 @@ function(expr1, ...)	function call
 | 
			
		||||
See below |functions|.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
lambda expression				*expr-lambda* *lambda*
 | 
			
		||||
-----------------
 | 
			
		||||
{args -> expr1}		lambda expression
 | 
			
		||||
 | 
			
		||||
A lambda expression creates a new unnamed function which returns the result of
 | 
			
		||||
evaluating |expr1|.  Lambda expressions are differ from |user-functions| in
 | 
			
		||||
the following ways:
 | 
			
		||||
 | 
			
		||||
1. The body of the lambda expression is an |expr1| and not a sequence of |Ex|
 | 
			
		||||
   commands.
 | 
			
		||||
2. The prefix "a:" is optional for arguments.  E.g.: >
 | 
			
		||||
	:let F = {arg1, arg2 -> arg1 - arg2}
 | 
			
		||||
	:echo F(5, 2)
 | 
			
		||||
<	3
 | 
			
		||||
 | 
			
		||||
The arguments are optional.  Example: >
 | 
			
		||||
	:let F = {-> 'error function'}
 | 
			
		||||
	:echo F()
 | 
			
		||||
<	error function
 | 
			
		||||
 | 
			
		||||
Examples for using a lambda expression with |sort()|, |map()| and |filter()|: >
 | 
			
		||||
	:echo map([1, 2, 3], {idx, val -> val + 1})
 | 
			
		||||
<	[2, 3, 4] >
 | 
			
		||||
	:echo sort([3,7,2,1,4], {a, b -> a - b})
 | 
			
		||||
<	[1, 2, 3, 4, 7]
 | 
			
		||||
 | 
			
		||||
The lambda expression is also useful for Channel, Job and timer: >
 | 
			
		||||
	:let timer = timer_start(500,
 | 
			
		||||
			\ {-> execute("echo 'Handler called'", "")},
 | 
			
		||||
			\ {'repeat': 3})
 | 
			
		||||
<	Handler called
 | 
			
		||||
	Handler called
 | 
			
		||||
	Handler called
 | 
			
		||||
 | 
			
		||||
Note how execute() is used to execute an Ex command.  That's ugly though.
 | 
			
		||||
 | 
			
		||||
==============================================================================
 | 
			
		||||
3. Internal variable				*internal-variables* *E461*
 | 
			
		||||
 | 
			
		||||
@@ -8094,10 +8132,10 @@ can be 0).  "a:000" is set to a |List| that contains these arguments.  Note
 | 
			
		||||
that "a:1" is the same as "a:000[0]".
 | 
			
		||||
								*E742*
 | 
			
		||||
The a: scope and the variables in it cannot be changed, they are fixed.
 | 
			
		||||
However, if a |List| or |Dictionary| is used, you can change their contents.
 | 
			
		||||
Thus you can pass a |List| to a function and have the function add an item to
 | 
			
		||||
it.  If you want to make sure the function cannot change a |List| or
 | 
			
		||||
|Dictionary| use |:lockvar|.
 | 
			
		||||
However, if a composite type is used, such as |List| or |Dictionary| , you can
 | 
			
		||||
change their contents.  Thus you can pass a |List| to a function and have the
 | 
			
		||||
function add an item to it.  If you want to make sure the function cannot
 | 
			
		||||
change a |List| or |Dictionary| use |:lockvar|.
 | 
			
		||||
 | 
			
		||||
When not using "...", the number of arguments in a function call must be equal
 | 
			
		||||
to the number of named arguments.  When using "...", the number of arguments
 | 
			
		||||
@@ -8109,9 +8147,8 @@ until the matching |:endfunction|.  It is allowed to define another function
 | 
			
		||||
inside a function body.
 | 
			
		||||
 | 
			
		||||
							*local-variables*
 | 
			
		||||
Inside a function variables can be used.  These are local variables, which
 | 
			
		||||
will disappear when the function returns.  Global variables need to be
 | 
			
		||||
accessed with "g:".
 | 
			
		||||
Inside a function local variables can be used.  These will disappear when the
 | 
			
		||||
function returns. Global variables need to be accessed with "g:".
 | 
			
		||||
 | 
			
		||||
Example: >
 | 
			
		||||
  :function Table(title, ...)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										362
									
								
								src/nvim/eval.c
									
									
									
									
									
								
							
							
						
						
									
										362
									
								
								src/nvim/eval.c
									
									
									
									
									
								
							@@ -4301,8 +4301,12 @@ static int eval7(
 | 
			
		||||
  case '[':   ret = get_list_tv(arg, rettv, evaluate);
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  // Lambda: {arg, arg -> expr}
 | 
			
		||||
  // Dictionary: {key: val, key: val}
 | 
			
		||||
  case '{':   ret = get_dict_tv(arg, rettv, evaluate);
 | 
			
		||||
  case '{':   ret = get_lambda_tv(arg, rettv, evaluate);
 | 
			
		||||
              if (ret == NOTDONE) {
 | 
			
		||||
                ret = get_dict_tv(arg, rettv, evaluate);
 | 
			
		||||
              }
 | 
			
		||||
    break;
 | 
			
		||||
 | 
			
		||||
  // Option value: &name
 | 
			
		||||
@@ -6871,6 +6875,202 @@ failret:
 | 
			
		||||
  return OK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Get function arguments.
 | 
			
		||||
static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs,
 | 
			
		||||
                             int *varargs, int skip)
 | 
			
		||||
{
 | 
			
		||||
  bool    mustend = false;
 | 
			
		||||
  char_u  *arg = *argp;
 | 
			
		||||
  char_u  *p = arg;
 | 
			
		||||
  int     c;
 | 
			
		||||
  int     i;
 | 
			
		||||
 | 
			
		||||
  if (newargs != NULL) {
 | 
			
		||||
    ga_init(newargs, (int)sizeof(char_u *), 3);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (varargs != NULL) {
 | 
			
		||||
    *varargs = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Isolate the arguments: "arg1, arg2, ...)"
 | 
			
		||||
  while (*p != endchar) {
 | 
			
		||||
    if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
 | 
			
		||||
      if (varargs != NULL) {
 | 
			
		||||
        *varargs = true;
 | 
			
		||||
      }
 | 
			
		||||
      p += 3;
 | 
			
		||||
      mustend = true;
 | 
			
		||||
    } else {
 | 
			
		||||
      arg = p;
 | 
			
		||||
      while (ASCII_ISALNUM(*p) || *p == '_') {
 | 
			
		||||
        p++;
 | 
			
		||||
      }
 | 
			
		||||
      if (arg == p || isdigit(*arg)
 | 
			
		||||
          || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
 | 
			
		||||
          || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
 | 
			
		||||
        if (!skip) {
 | 
			
		||||
          EMSG2(_("E125: Illegal argument: %s"), arg);
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (newargs != NULL) {
 | 
			
		||||
        ga_grow(newargs, 1);
 | 
			
		||||
        c = *p;
 | 
			
		||||
        *p = NUL;
 | 
			
		||||
        arg = vim_strsave(arg);
 | 
			
		||||
        if (arg == NULL) {
 | 
			
		||||
          goto err_ret;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Check for duplicate argument name.
 | 
			
		||||
        for (i = 0; i < newargs->ga_len; i++) {
 | 
			
		||||
          if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0) {
 | 
			
		||||
            EMSG2(_("E853: Duplicate argument name: %s"), arg);
 | 
			
		||||
            xfree(arg);
 | 
			
		||||
            goto err_ret;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg;
 | 
			
		||||
        newargs->ga_len++;
 | 
			
		||||
 | 
			
		||||
        *p = c;
 | 
			
		||||
      }
 | 
			
		||||
      if (*p == ',') {
 | 
			
		||||
        p++;
 | 
			
		||||
      } else {
 | 
			
		||||
        mustend = true;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    p = skipwhite(p);
 | 
			
		||||
    if (mustend && *p != endchar) {
 | 
			
		||||
      if (!skip) {
 | 
			
		||||
        EMSG2(_(e_invarg2), *argp);
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (*p != endchar) {
 | 
			
		||||
    goto err_ret;
 | 
			
		||||
  }
 | 
			
		||||
  p++;  // skip "endchar"
 | 
			
		||||
 | 
			
		||||
  *argp = p;
 | 
			
		||||
  return OK;
 | 
			
		||||
 | 
			
		||||
err_ret:
 | 
			
		||||
  if (newargs != NULL) {
 | 
			
		||||
    ga_clear_strings(newargs);
 | 
			
		||||
  }
 | 
			
		||||
  return FAIL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse a lambda expression and get a Funcref from "*arg".
 | 
			
		||||
/// Return OK or FAIL.  Returns NOTDONE for dict or {expr}.
 | 
			
		||||
static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate)
 | 
			
		||||
{
 | 
			
		||||
  garray_T   newargs;
 | 
			
		||||
  garray_T   newlines;
 | 
			
		||||
  ufunc_T    *fp = NULL;
 | 
			
		||||
  int        varargs;
 | 
			
		||||
  int        ret;
 | 
			
		||||
  char_u     name[20];
 | 
			
		||||
  char_u     *start = skipwhite(*arg + 1);
 | 
			
		||||
  char_u     *s, *e;
 | 
			
		||||
  static int lambda_no = 0;
 | 
			
		||||
 | 
			
		||||
  // TODO(mike): What lengths should be used here?
 | 
			
		||||
  ga_init(&newargs, (int)sizeof(char_u *), 80);
 | 
			
		||||
  ga_init(&newlines, (int)sizeof(char_u *), 80);
 | 
			
		||||
 | 
			
		||||
  // First, check if this is a lambda expression. "->" must exists.
 | 
			
		||||
  ret = get_function_args(&start, '-', NULL, NULL, true);
 | 
			
		||||
  if (ret == FAIL || *start != '>') {
 | 
			
		||||
    return NOTDONE;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Parse the arguments again.
 | 
			
		||||
  *arg = skipwhite(*arg + 1);
 | 
			
		||||
  ret = get_function_args(arg, '-', &newargs, &varargs, false);
 | 
			
		||||
  if (ret == FAIL || **arg != '>') {
 | 
			
		||||
    goto errret;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Get the start and the end of the expression.
 | 
			
		||||
  *arg = skipwhite(*arg + 1);
 | 
			
		||||
  s = *arg;
 | 
			
		||||
  ret = skip_expr(arg);
 | 
			
		||||
  if (ret == FAIL) {
 | 
			
		||||
    goto errret;
 | 
			
		||||
  }
 | 
			
		||||
  e = *arg;
 | 
			
		||||
  *arg = skipwhite(*arg);
 | 
			
		||||
  if (**arg != '}') {
 | 
			
		||||
    goto errret;
 | 
			
		||||
  }
 | 
			
		||||
  (*arg)++;
 | 
			
		||||
 | 
			
		||||
  if (evaluate) {
 | 
			
		||||
    int len;
 | 
			
		||||
    char_u *p;
 | 
			
		||||
 | 
			
		||||
    fp = (ufunc_T *)xmalloc((unsigned)(sizeof(ufunc_T) + 20));
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
      goto errret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    snprintf((char *)name, sizeof(name), "<lambda>%d", lambda_no++);
 | 
			
		||||
 | 
			
		||||
    ga_init(&newlines, (int)sizeof(char_u *), 1);
 | 
			
		||||
    ga_grow(&newlines, 1);
 | 
			
		||||
 | 
			
		||||
    // Add "return " before the expression.
 | 
			
		||||
    // TODO(vim): Support multiple expressions.
 | 
			
		||||
    len = 7 + e - s + 1;
 | 
			
		||||
    p = (char_u *)xmalloc(len);
 | 
			
		||||
    if (p == NULL) {
 | 
			
		||||
      goto errret;
 | 
			
		||||
    }
 | 
			
		||||
    ((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
 | 
			
		||||
    STRCPY(p, "return ");
 | 
			
		||||
    STRNCPY(p + 7, s, e - s);
 | 
			
		||||
    p[7 + e - s] = NUL;
 | 
			
		||||
 | 
			
		||||
    fp->uf_refcount = 1;
 | 
			
		||||
    STRCPY(fp->uf_name, name);
 | 
			
		||||
    hash_add(&func_hashtab, UF2HIKEY(fp));
 | 
			
		||||
    fp->uf_args = newargs;
 | 
			
		||||
    fp->uf_lines = newlines;
 | 
			
		||||
 | 
			
		||||
#ifdef FEAT_PROFILE
 | 
			
		||||
    fp->uf_tml_count = NULL;
 | 
			
		||||
    fp->uf_tml_total = NULL;
 | 
			
		||||
    fp->uf_tml_self = NULL;
 | 
			
		||||
    fp->uf_profiling = false;
 | 
			
		||||
    if (prof_def_func()) {
 | 
			
		||||
      func_do_profile(fp);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    fp->uf_varargs = true;
 | 
			
		||||
    fp->uf_flags = 0;
 | 
			
		||||
    fp->uf_calls = 0;
 | 
			
		||||
    fp->uf_script_ID = current_SID;
 | 
			
		||||
 | 
			
		||||
    rettv->vval.v_string = vim_strsave(name);
 | 
			
		||||
    rettv->v_type = VAR_FUNC;
 | 
			
		||||
    } else {
 | 
			
		||||
      ga_clear_strings(&newargs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return OK;
 | 
			
		||||
 | 
			
		||||
errret:
 | 
			
		||||
    ga_clear_strings(&newargs);
 | 
			
		||||
    ga_clear_strings(&newlines);
 | 
			
		||||
    xfree(fp);
 | 
			
		||||
    return FAIL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Convert the string to a floating point number
 | 
			
		||||
///
 | 
			
		||||
/// This uses strtod().  setlocale(LC_NUMERIC, "C") has been used earlier to
 | 
			
		||||
@@ -20495,8 +20695,7 @@ void ex_function(exarg_T *eap)
 | 
			
		||||
  char_u      *line_arg = NULL;
 | 
			
		||||
  garray_T newargs;
 | 
			
		||||
  garray_T newlines;
 | 
			
		||||
  int varargs = FALSE;
 | 
			
		||||
  int mustend = FALSE;
 | 
			
		||||
  int varargs = false;
 | 
			
		||||
  int flags = 0;
 | 
			
		||||
  ufunc_T     *fp;
 | 
			
		||||
  int indent;
 | 
			
		||||
@@ -20679,59 +20878,11 @@ void ex_function(exarg_T *eap)
 | 
			
		||||
      EMSG(_("E862: Cannot use g: here"));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Isolate the arguments: "arg1, arg2, ...)"
 | 
			
		||||
   */
 | 
			
		||||
  while (*p != ')') {
 | 
			
		||||
    if (p[0] == '.' && p[1] == '.' && p[2] == '.') {
 | 
			
		||||
      varargs = TRUE;
 | 
			
		||||
      p += 3;
 | 
			
		||||
      mustend = TRUE;
 | 
			
		||||
    } else {
 | 
			
		||||
      arg = p;
 | 
			
		||||
      while (ASCII_ISALNUM(*p) || *p == '_')
 | 
			
		||||
        ++p;
 | 
			
		||||
      if (arg == p || isdigit(*arg)
 | 
			
		||||
          || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0)
 | 
			
		||||
          || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) {
 | 
			
		||||
        if (!eap->skip)
 | 
			
		||||
          EMSG2(_("E125: Illegal argument: %s"), arg);
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      ga_grow(&newargs, 1);
 | 
			
		||||
      c = *p;
 | 
			
		||||
      *p = NUL;
 | 
			
		||||
      arg = vim_strsave(arg);
 | 
			
		||||
 | 
			
		||||
      /* Check for duplicate argument name. */
 | 
			
		||||
      for (int i = 0; i < newargs.ga_len; ++i)
 | 
			
		||||
        if (STRCMP(((char_u **)(newargs.ga_data))[i], arg) == 0) {
 | 
			
		||||
          EMSG2(_("E853: Duplicate argument name: %s"), arg);
 | 
			
		||||
          xfree(arg);
 | 
			
		||||
          goto erret;
 | 
			
		||||
  if (get_function_args(&p, ')', &newargs, &varargs, eap->skip) == FAIL) {
 | 
			
		||||
    goto errret_2;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
      ((char_u **)(newargs.ga_data))[newargs.ga_len] = arg;
 | 
			
		||||
      *p = c;
 | 
			
		||||
      newargs.ga_len++;
 | 
			
		||||
      if (*p == ',')
 | 
			
		||||
        ++p;
 | 
			
		||||
      else
 | 
			
		||||
        mustend = TRUE;
 | 
			
		||||
    }
 | 
			
		||||
    p = skipwhite(p);
 | 
			
		||||
    if (mustend && *p != ')') {
 | 
			
		||||
      if (!eap->skip)
 | 
			
		||||
        EMSG2(_(e_invarg2), eap->arg);
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (*p != ')') {
 | 
			
		||||
    goto erret;
 | 
			
		||||
  }
 | 
			
		||||
  ++p;  // skip the ')'
 | 
			
		||||
 | 
			
		||||
  /* find extra arguments "range", "dict" and "abort" */
 | 
			
		||||
  // find extra arguments "range", "dict" and "abort"
 | 
			
		||||
  for (;; ) {
 | 
			
		||||
    p = skipwhite(p);
 | 
			
		||||
    if (STRNCMP(p, "range", 5) == 0) {
 | 
			
		||||
@@ -21041,6 +21192,7 @@ void ex_function(exarg_T *eap)
 | 
			
		||||
 | 
			
		||||
erret:
 | 
			
		||||
  ga_clear_strings(&newargs);
 | 
			
		||||
errret_2:
 | 
			
		||||
  ga_clear_strings(&newlines);
 | 
			
		||||
ret_free:
 | 
			
		||||
  xfree(skip_until);
 | 
			
		||||
@@ -21764,7 +21916,9 @@ void func_unref(char_u *name)
 | 
			
		||||
{
 | 
			
		||||
  ufunc_T *fp;
 | 
			
		||||
 | 
			
		||||
  if (name != NULL && isdigit(*name)) {
 | 
			
		||||
  if (name == NULL) {
 | 
			
		||||
    return;
 | 
			
		||||
  } else if (isdigit(*name)) {
 | 
			
		||||
    fp = find_func(name);
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
#ifdef EXITFREE
 | 
			
		||||
@@ -21777,6 +21931,16 @@ void func_unref(char_u *name)
 | 
			
		||||
    } else {
 | 
			
		||||
      user_func_unref(fp);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (STRNCMP(name, "<lambda>", 8) == 0) {
 | 
			
		||||
    // fail silently, when lambda function isn't found
 | 
			
		||||
    fp = find_func(name);
 | 
			
		||||
    if (fp != NULL && --fp->uf_refcount <= 0) {
 | 
			
		||||
      // Only delete it when it's not being used. Otherwise it's done
 | 
			
		||||
      // when "uf_calls" becomes zero.
 | 
			
		||||
      if (fp->uf_calls == 0) {
 | 
			
		||||
        func_free(fp);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -21798,12 +21962,21 @@ void func_ref(char_u *name)
 | 
			
		||||
{
 | 
			
		||||
  ufunc_T *fp;
 | 
			
		||||
 | 
			
		||||
  if (name != NULL && isdigit(*name)) {
 | 
			
		||||
  if (name == NULL) {
 | 
			
		||||
    return;
 | 
			
		||||
  } else if (isdigit(*name)) {
 | 
			
		||||
    fp = find_func(name);
 | 
			
		||||
    if (fp == NULL)
 | 
			
		||||
    if (fp == NULL) {
 | 
			
		||||
      EMSG2(_(e_intern2), "func_ref()");
 | 
			
		||||
    else
 | 
			
		||||
      ++fp->uf_refcount;
 | 
			
		||||
    } else {
 | 
			
		||||
      (fp->uf_refcount)++;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (STRNCMP(name, "<lambda>", 8) == 0) {
 | 
			
		||||
    // fail silently, when lambda function isn't found.
 | 
			
		||||
    fp = find_func(name);
 | 
			
		||||
    if (fp != NULL) {
 | 
			
		||||
      (fp->uf_refcount)++;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -21830,6 +22003,7 @@ call_user_func (
 | 
			
		||||
  dictitem_T  *v;
 | 
			
		||||
  int fixvar_idx = 0;           /* index in fixvar[] */
 | 
			
		||||
  int ai;
 | 
			
		||||
  bool islambda = false;
 | 
			
		||||
  char_u numbuf[NUMBUFLEN];
 | 
			
		||||
  char_u      *name;
 | 
			
		||||
  proftime_T wait_start;
 | 
			
		||||
@@ -21867,14 +22041,15 @@ call_user_func (
 | 
			
		||||
  fc->breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0);
 | 
			
		||||
  fc->dbg_tick = debug_tick;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
 | 
			
		||||
   * with names up to VAR_SHORT_LEN long.  This avoids having to alloc/free
 | 
			
		||||
   * each argument variable and saves a lot of time.
 | 
			
		||||
   */
 | 
			
		||||
  /*
 | 
			
		||||
   * Init l: variables.
 | 
			
		||||
   */
 | 
			
		||||
  if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) {
 | 
			
		||||
    islambda = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Note about using fc->fixvar[]: This is an array of FIXVAR_CNT variables
 | 
			
		||||
  // with names up to VAR_SHORT_LEN long.  This avoids having to alloc/free
 | 
			
		||||
  // each argument variable and saves a lot of time.
 | 
			
		||||
  //
 | 
			
		||||
  // Init l: variables.
 | 
			
		||||
  init_var_dict(&fc->l_vars, &fc->l_vars_var, VAR_DEF_SCOPE);
 | 
			
		||||
  if (selfdict != NULL) {
 | 
			
		||||
    /* Set l:self to "selfdict".  Use "name" to avoid a warning from
 | 
			
		||||
@@ -21916,31 +22091,49 @@ call_user_func (
 | 
			
		||||
  fc->l_varlist.lv_refcount = DO_NOT_FREE_CNT;
 | 
			
		||||
  fc->l_varlist.lv_lock = VAR_FIXED;
 | 
			
		||||
 | 
			
		||||
  /*
 | 
			
		||||
   * Set a:firstline to "firstline" and a:lastline to "lastline".
 | 
			
		||||
   * Set a:name to named arguments.
 | 
			
		||||
   * Set a:N to the "..." arguments.
 | 
			
		||||
   */
 | 
			
		||||
  // Set a:firstline to "firstline" and a:lastline to "lastline".
 | 
			
		||||
  // Set a:name to named arguments.
 | 
			
		||||
  // Set a:N to the "..." arguments.
 | 
			
		||||
  add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "firstline",
 | 
			
		||||
             (varnumber_T)firstline);
 | 
			
		||||
  add_nr_var(&fc->l_avars, &fc->fixvar[fixvar_idx++].var, "lastline",
 | 
			
		||||
             (varnumber_T)lastline);
 | 
			
		||||
  for (int i = 0; i < argcount; ++i) {
 | 
			
		||||
  for (int i = 0; i < argcount; i++) {
 | 
			
		||||
    bool addlocal = false;
 | 
			
		||||
    dictitem_T *v2;
 | 
			
		||||
 | 
			
		||||
    ai = i - fp->uf_args.ga_len;
 | 
			
		||||
    if (ai < 0)
 | 
			
		||||
      /* named argument a:name */
 | 
			
		||||
    if (ai < 0) {
 | 
			
		||||
      // named argument a:name
 | 
			
		||||
      name = FUNCARG(fp, i);
 | 
			
		||||
    else {
 | 
			
		||||
      /* "..." argument a:1, a:2, etc. */
 | 
			
		||||
      sprintf((char *)numbuf, "%d", ai + 1);
 | 
			
		||||
      if (islambda) {
 | 
			
		||||
        addlocal = true;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      // "..." argument a:1, a:2, etc.
 | 
			
		||||
      snprintf((char *)numbuf, sizeof(numbuf), "%d", ai + 1);
 | 
			
		||||
      name = numbuf;
 | 
			
		||||
    }
 | 
			
		||||
    if (fixvar_idx < FIXVAR_CNT && STRLEN(name) <= VAR_SHORT_LEN) {
 | 
			
		||||
      v = &fc->fixvar[fixvar_idx++].var;
 | 
			
		||||
      v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX;
 | 
			
		||||
 | 
			
		||||
      if (addlocal) {
 | 
			
		||||
        v2 = v;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      v = xmalloc(sizeof(dictitem_T) + STRLEN(name));
 | 
			
		||||
      v->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
 | 
			
		||||
 | 
			
		||||
      if (addlocal) {
 | 
			
		||||
        v2 = (dictitem_T *)xmalloc((unsigned)(sizeof(dictitem_T)
 | 
			
		||||
                                              + STRLEN(name)));
 | 
			
		||||
        if (v2 == NULL) {
 | 
			
		||||
          xfree(v);
 | 
			
		||||
          break;
 | 
			
		||||
        }
 | 
			
		||||
        v2->di_flags = DI_FLAGS_RO | DI_FLAGS_FIX | DI_FLAGS_ALLOC;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    STRCPY(v->di_key, name);
 | 
			
		||||
    hash_add(&fc->l_avars.dv_hashtab, DI2HIKEY(v));
 | 
			
		||||
@@ -21950,6 +22143,15 @@ call_user_func (
 | 
			
		||||
    v->di_tv = argvars[i];
 | 
			
		||||
    v->di_tv.v_lock = VAR_FIXED;
 | 
			
		||||
 | 
			
		||||
    // Named arguments can be accessed without the "a:" prefix in lambda
 | 
			
		||||
    // expressions. Add to the l: dict.
 | 
			
		||||
    if (addlocal) {
 | 
			
		||||
      STRCPY(v2->di_key, name);
 | 
			
		||||
      copy_tv(&v->di_tv, &v2->di_tv);
 | 
			
		||||
      v2->di_tv.v_lock = VAR_FIXED;
 | 
			
		||||
      hash_add(&fc->l_vars.dv_hashtab, DI2HIKEY(v2));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (ai >= 0 && ai < MAX_FUNC_ARGS) {
 | 
			
		||||
      list_append(&fc->l_varlist, &fc->l_listitems[ai]);
 | 
			
		||||
      fc->l_listitems[ai].li_tv = argvars[i];
 | 
			
		||||
@@ -22167,7 +22369,9 @@ call_user_func (
 | 
			
		||||
      copy_tv(&li->li_tv, &li->li_tv);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (--fp->uf_calls <= 0 && isdigit(*fp->uf_name) && fp->uf_refcount <= 0) {
 | 
			
		||||
  if (--fp->uf_calls <= 0 && (isdigit(*fp->uf_name)
 | 
			
		||||
                              || STRNCMP(fp->uf_name, "<lambda>", 8) == 0)
 | 
			
		||||
      && fp->uf_refcount <= 0) {
 | 
			
		||||
    // Function was unreferenced while being used, free it now.
 | 
			
		||||
    func_free(fp);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -41,6 +41,7 @@ NEW_TESTS ?= \
 | 
			
		||||
	    test_history.res \
 | 
			
		||||
	    test_increment.res \
 | 
			
		||||
	    test_increment_dbcs.res \
 | 
			
		||||
		test_lambda.res \
 | 
			
		||||
	    test_langmap.res \
 | 
			
		||||
	    test_match.res \
 | 
			
		||||
	    test_matchadd_conceal.res \
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ source test_feedkeys.vim
 | 
			
		||||
source test_filter_map.vim
 | 
			
		||||
source test_goto.vim
 | 
			
		||||
source test_jumps.vim
 | 
			
		||||
source test_lambda.vim
 | 
			
		||||
source test_match.vim
 | 
			
		||||
source test_matchadd_conceal_utf8.vim
 | 
			
		||||
source test_menu.vim
 | 
			
		||||
 
 | 
			
		||||
@@ -284,3 +284,5 @@ func Test_named_function_closure()
 | 
			
		||||
  call garbagecollect()
 | 
			
		||||
  call assert_equal(14, s:Abar())
 | 
			
		||||
endfunc
 | 
			
		||||
=======
 | 
			
		||||
>>>>>>> 381a8796... vim-patch:7.4.2044
 | 
			
		||||
 
 | 
			
		||||
@@ -396,7 +396,7 @@ static int included_patches[] = {
 | 
			
		||||
  // 2047,
 | 
			
		||||
  // 2046,
 | 
			
		||||
  // 2045 NA
 | 
			
		||||
  // 2044,
 | 
			
		||||
  2044,
 | 
			
		||||
  2043,
 | 
			
		||||
  // 2042 NA
 | 
			
		||||
  // 2041 NA
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user