]> granicus.if.org Git - vim/commitdiff
patch 7.4.1559 v7.4.1559
authorBram Moolenaar <Bram@vim.org>
Mon, 14 Mar 2016 22:05:14 +0000 (23:05 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 14 Mar 2016 22:05:14 +0000 (23:05 +0100)
Problem:    Passing cookie to a callback is clumsy.
Solution:   Change function() to take arguments and return a partial.

12 files changed:
runtime/doc/eval.txt
src/channel.c
src/eval.c
src/if_py_both.h
src/if_python.c
src/if_python3.c
src/json.c
src/proto/eval.pro
src/structs.h
src/testdir/test_alot.vim
src/testdir/test_partial.vim [new file with mode: 0644]
src/version.c

index 14607310c309493ff2fb1f3d0a7a1b97a4c140e3..6cc82b445c2aea653a032893333ae687a16d68e3 100644 (file)
@@ -1,4 +1,4 @@
-*eval.txt*     For Vim version 7.4.  Last change: 2016 Mar 13
+*eval.txt*     For Vim version 7.4.  Last change: 2016 Mar 14
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -1895,7 +1895,8 @@ foldlevel( {lnum})                Number  fold level at {lnum}
 foldtext()                     String  line displayed for closed fold
 foldtextresult( {lnum})                String  text for closed fold at {lnum}
 foreground()                   Number  bring the Vim window to the foreground
-function( {name})              Funcref reference to function {name}
+function({name} [, {arglist}] [, {dict}])
+                               Funcref reference to function {name}
 garbagecollect( [{atexit}])    none    free memory, breaking cyclic references
 get( {list}, {idx} [, {def}])  any     get item {idx} from {list} or {def}
 get( {dict}, {key} [, {def}])  any     get item {key} from {dict} or {def}
@@ -3568,10 +3569,46 @@ foreground()    Move the Vim window to the foreground.  Useful when sent from
                Win32 console version}
 
 
-function({name})                                       *function()* *E700*
+                                       *function()* *E700* *E922* *E923*
+function({name} [, {arglist}] [, {dict}])
                Return a |Funcref| variable that refers to function {name}.
                {name} can be a user defined function or an internal function.
 
+               When {arglist} or {dict} is present this creates a partial.
+               That mans the argument list and/or the dictionary is stored in
+               the Funcref and will be used when the Funcref is called.
+               
+               The arguments are passed to the function in front of other
+               arguments.  Example: >
+                       func Callback(arg1, arg2, name)
+                       ...
+                       let Func = function('Callback', ['one', 'two'])
+                       ...
+                       call Func('name')
+<              Invokes the function as with: >
+                       call Callback('one', 'two', 'name')
+
+<              The Dictionary is only useful when calling a "dict" function.
+               In that case the {dict} is passed in as "self". Example: >
+                       function Callback() dict
+                          echo "called for " . self.name
+                       endfunction
+                       ...
+                       let context = {"name": "example"}
+                       let Func = function('Callback', context)
+                       ...
+                       call Func()     " will echo: called for example
+
+<              The argument list and the Dictionary can be combined: >
+                       function Callback(arg1, count) dict
+                       ...
+                       let context = {"name": "example"}
+                       let Func = function('Callback', ['one'], context)
+                       ...
+                       call Func(500)
+<              Invokes the function as with: >
+                       call context.Callback('one', 500)
+
 
 garbagecollect([{atexit}])                             *garbagecollect()*
                Cleanup unused |Lists| and |Dictionaries| that have circular
index 99f053a2f493528f684f5d9a26529d35c5dd24e1..81823bc064c16bdb2afe19abc72e3d676b582cf8 100644 (file)
@@ -1025,8 +1025,9 @@ find_buffer(char_u *name, int err)
     void
 channel_set_options(channel_T *channel, jobopt_T *opt)
 {
-    int    part;
-    char_u **cbp;
+    int                part;
+    char_u     **cbp;
+    partial_T  **pp;
 
     if (opt->jo_set & JO_MODE)
        for (part = PART_SOCK; part <= PART_IN; ++part)
@@ -1049,38 +1050,58 @@ channel_set_options(channel_T *channel, jobopt_T *opt)
     if (opt->jo_set & JO_CALLBACK)
     {
        cbp = &channel->ch_callback;
+       pp = &channel->ch_partial;
        vim_free(*cbp);
+       partial_unref(*pp);
        if (opt->jo_callback != NULL && *opt->jo_callback != NUL)
            *cbp = vim_strsave(opt->jo_callback);
        else
            *cbp = NULL;
+       *pp = opt->jo_partial;
+       if (*pp != NULL)
+           ++(*pp)->pt_refcount;
     }
     if (opt->jo_set & JO_OUT_CALLBACK)
     {
        cbp = &channel->ch_part[PART_OUT].ch_callback;
+       pp = &channel->ch_part[PART_OUT].ch_partial;
        vim_free(*cbp);
+       partial_unref(*pp);
        if (opt->jo_out_cb != NULL && *opt->jo_out_cb != NUL)
            *cbp = vim_strsave(opt->jo_out_cb);
        else
            *cbp = NULL;
+       *pp = opt->jo_out_partial;
+       if (*pp != NULL)
+           ++(*pp)->pt_refcount;
     }
     if (opt->jo_set & JO_ERR_CALLBACK)
     {
        cbp = &channel->ch_part[PART_ERR].ch_callback;
+       pp = &channel->ch_part[PART_ERR].ch_partial;
        vim_free(*cbp);
+       partial_unref(*pp);
        if (opt->jo_err_cb != NULL && *opt->jo_err_cb != NUL)
            *cbp = vim_strsave(opt->jo_err_cb);
        else
            *cbp = NULL;
+       *pp = opt->jo_err_partial;
+       if (*pp != NULL)
+           ++(*pp)->pt_refcount;
     }
     if (opt->jo_set & JO_CLOSE_CALLBACK)
     {
        cbp = &channel->ch_close_cb;
+       pp = &channel->ch_close_partial;
        vim_free(*cbp);
+       partial_unref(*pp);
        if (opt->jo_close_cb != NULL && *opt->jo_close_cb != NUL)
            *cbp = vim_strsave(opt->jo_close_cb);
        else
            *cbp = NULL;
+       *pp = opt->jo_err_partial;
+       if (*pp != NULL)
+           ++(*pp)->pt_refcount;
     }
 
     if ((opt->jo_set & JO_OUT_IO) && opt->jo_io[PART_OUT] == JIO_BUFFER)
@@ -1124,10 +1145,11 @@ channel_set_options(channel_T *channel, jobopt_T *opt)
  */
     void
 channel_set_req_callback(
-       channel_T *channel,
-       int part,
-       char_u *callback,
-       int id)
+       channel_T   *channel,
+       int         part,
+       char_u      *callback,
+       partial_T   *partial,
+       int         id)
 {
     cbq_T *head = &channel->ch_part[part].ch_cb_head;
     cbq_T *item = (cbq_T *)alloc((int)sizeof(cbq_T));
@@ -1135,6 +1157,9 @@ channel_set_req_callback(
     if (item != NULL)
     {
        item->cq_callback = vim_strsave(callback);
+       item->cq_partial = partial;
+       if (partial != NULL)
+           ++partial->pt_refcount;
        item->cq_seq_nr = id;
        item->cq_prev = head->cq_prev;
        head->cq_prev = item;
@@ -1247,7 +1272,8 @@ channel_write_new_lines(buf_T *buf)
  * Invoke the "callback" on channel "channel".
  */
     static void
-invoke_callback(channel_T *channel, char_u *callback, typval_T *argv)
+invoke_callback(channel_T *channel, char_u *callback, partial_T *partial,
+                                                              typval_T *argv)
 {
     typval_T   rettv;
     int                dummy;
@@ -1256,7 +1282,7 @@ invoke_callback(channel_T *channel, char_u *callback, typval_T *argv)
     argv[0].vval.v_channel = channel;
 
     call_func(callback, (int)STRLEN(callback),
-                            &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
+                       &rettv, 2, argv, 0L, 0L, &dummy, TRUE, partial, NULL);
     clear_tv(&rettv);
 
     /* If an echo command was used the cursor needs to be put back where
@@ -1629,7 +1655,7 @@ channel_exe_cmd(channel_T *channel, int part, typval_T *argv)
            ++emsg_skip;
            if (!is_call)
                tv = eval_expr(arg, NULL);
-           else if (func_call(arg, &argv[2], NULL, &res_tv) == OK)
+           else if (func_call(arg, &argv[2], NULL, NULL, &res_tv) == OK)
                tv = &res_tv;
            else
                tv = NULL;
@@ -1685,8 +1711,9 @@ invoke_one_time_callback(
     /* Remove the item from the list first, if the callback
      * invokes ch_close() the list will be cleared. */
     remove_cb_node(cbhead, item);
-    invoke_callback(channel, item->cq_callback, argv);
+    invoke_callback(channel, item->cq_callback, item->cq_partial, argv);
     vim_free(item->cq_callback);
+    partial_unref(item->cq_partial);
     vim_free(item);
 }
 
@@ -1775,6 +1802,7 @@ may_invoke_callback(channel_T *channel, int part)
     cbq_T      *cbhead = &channel->ch_part[part].ch_cb_head;
     cbq_T      *cbitem;
     char_u     *callback = NULL;
+    partial_T  *partial = NULL;
     buf_T      *buffer = NULL;
 
     if (channel->ch_nb_close_cb != NULL)
@@ -1786,11 +1814,20 @@ may_invoke_callback(channel_T *channel, int part)
        if (cbitem->cq_seq_nr == 0)
            break;
     if (cbitem != NULL)
+    {
        callback = cbitem->cq_callback;
+       partial = cbitem->cq_partial;
+    }
     else if (channel->ch_part[part].ch_callback != NULL)
+    {
        callback = channel->ch_part[part].ch_callback;
+       partial = channel->ch_part[part].ch_partial;
+    }
     else
+    {
        callback = channel->ch_callback;
+       partial = channel->ch_partial;
+    }
 
     buffer = channel->ch_part[part].ch_buffer;
     if (buffer != NULL && !buf_valid(buffer))
@@ -1936,7 +1973,7 @@ may_invoke_callback(channel_T *channel, int part)
                /* invoke the channel callback */
                ch_logs(channel, "Invoking channel callback %s",
                                                            (char *)callback);
-               invoke_callback(channel, callback, argv);
+               invoke_callback(channel, callback, partial, argv);
            }
        }
     }
@@ -2024,13 +2061,16 @@ channel_close(channel_T *channel, int invoke_close_cb)
          argv[0].vval.v_channel = channel;
          ++channel->ch_refcount;
          call_func(channel->ch_close_cb, (int)STRLEN(channel->ch_close_cb),
-                                &rettv, 1, argv, 0L, 0L, &dummy, TRUE, NULL);
+                          &rettv, 1, argv, 0L, 0L, &dummy, TRUE,
+                          channel->ch_close_partial, NULL);
          clear_tv(&rettv);
          --channel->ch_refcount;
 
          /* the callback is only called once */
          vim_free(channel->ch_close_cb);
          channel->ch_close_cb = NULL;
+         partial_unref(channel->ch_close_partial);
+         channel->ch_close_partial = NULL;
     }
 
     channel->ch_nb_close_cb = NULL;
@@ -2068,6 +2108,7 @@ channel_clear_one(channel_T *channel, int part)
 
        remove_cb_node(cb_head, node);
        vim_free(node->cq_callback);
+       partial_unref(node->cq_partial);
        vim_free(node);
     }
 
@@ -2079,6 +2120,8 @@ channel_clear_one(channel_T *channel, int part)
 
     vim_free(channel->ch_part[part].ch_callback);
     channel->ch_part[part].ch_callback = NULL;
+    partial_unref(channel->ch_part[part].ch_partial);
+    channel->ch_part[part].ch_partial = NULL;
 }
 
 /*
@@ -2093,8 +2136,12 @@ channel_clear(channel_T *channel)
     channel_clear_one(channel, PART_ERR);
     vim_free(channel->ch_callback);
     channel->ch_callback = NULL;
+    partial_unref(channel->ch_partial);
+    channel->ch_partial = NULL;
     vim_free(channel->ch_close_cb);
     channel->ch_close_cb = NULL;
+    partial_unref(channel->ch_close_partial);
+    channel->ch_close_partial = NULL;
 }
 
 #if defined(EXITFREE) || defined(PROTO)
@@ -2592,7 +2639,8 @@ send_common(
            EMSG2(_("E917: Cannot use a callback with %s()"), fun);
            return NULL;
        }
-       channel_set_req_callback(channel, part_send, opt->jo_callback, id);
+       channel_set_req_callback(channel, part_send,
+                                      opt->jo_callback, opt->jo_partial, id);
     }
 
     if (channel_send(channel, part_send, text, fun) == OK
@@ -2982,13 +3030,19 @@ channel_get_timeout(channel_T *channel, int part)
  * Return NULL for an invalid argument.
  */
     static char_u *
-get_callback(typval_T *arg)
+get_callback(typval_T *arg, partial_T **pp)
 {
+    if (arg->v_type == VAR_PARTIAL && arg->vval.v_partial != NULL)
+    {
+       *pp = arg->vval.v_partial;
+       return (*pp)->pt_name;
+    }
+    *pp = NULL;
     if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
        return arg->vval.v_string;
     if (arg->v_type == VAR_NUMBER && arg->vval.v_number == 0)
        return (char_u *)"";
-    EMSG(_("E999: Invalid callback argument"));
+    EMSG(_("E921: Invalid callback argument"));
     return NULL;
 }
 
@@ -3201,7 +3255,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                if (!(supported & JO_CALLBACK))
                    break;
                opt->jo_set |= JO_CALLBACK;
-               opt->jo_callback = get_callback(item);
+               opt->jo_callback = get_callback(item, &opt->jo_partial);
                if (opt->jo_callback == NULL)
                {
                    EMSG2(_(e_invarg2), "callback");
@@ -3213,7 +3267,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                if (!(supported & JO_OUT_CALLBACK))
                    break;
                opt->jo_set |= JO_OUT_CALLBACK;
-               opt->jo_out_cb = get_callback(item);
+               opt->jo_out_cb = get_callback(item, &opt->jo_out_partial);
                if (opt->jo_out_cb == NULL)
                {
                    EMSG2(_(e_invarg2), "out-cb");
@@ -3225,7 +3279,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                if (!(supported & JO_ERR_CALLBACK))
                    break;
                opt->jo_set |= JO_ERR_CALLBACK;
-               opt->jo_err_cb = get_callback(item);
+               opt->jo_err_cb = get_callback(item, &opt->jo_err_partial);
                if (opt->jo_err_cb == NULL)
                {
                    EMSG2(_(e_invarg2), "err-cb");
@@ -3237,7 +3291,7 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                if (!(supported & JO_CLOSE_CALLBACK))
                    break;
                opt->jo_set |= JO_CLOSE_CALLBACK;
-               opt->jo_close_cb = get_callback(item);
+               opt->jo_close_cb = get_callback(item, &opt->jo_close_partial);
                if (opt->jo_close_cb == NULL)
                {
                    EMSG2(_(e_invarg2), "close-cb");
@@ -3311,7 +3365,14 @@ get_job_options(typval_T *tv, jobopt_T *opt, int supported)
                if (!(supported & JO_EXIT_CB))
                    break;
                opt->jo_set |= JO_EXIT_CB;
-               opt->jo_exit_cb = get_tv_string_buf_chk(item, opt->jo_ecb_buf);
+               if (item->v_type == VAR_PARTIAL && item->vval.v_partial != NULL)
+               {
+                   opt->jo_exit_partial = item->vval.v_partial;
+                   opt->jo_exit_cb = item->vval.v_partial->pt_name;
+               }
+               else
+                   opt->jo_exit_cb = get_tv_string_buf_chk(
+                                                      item, opt->jo_ecb_buf);
                if (opt->jo_exit_cb == NULL)
                {
                    EMSG2(_(e_invarg2), "exit-cb");
@@ -3390,6 +3451,7 @@ job_free(job_T *job)
 
     vim_free(job->jv_stoponexit);
     vim_free(job->jv_exit_cb);
+    partial_unref(job->jv_exit_partial);
     vim_free(job);
 }
 
@@ -3454,10 +3516,19 @@ job_set_options(job_T *job, jobopt_T *opt)
     if (opt->jo_set & JO_EXIT_CB)
     {
        vim_free(job->jv_exit_cb);
+       partial_unref(job->jv_exit_partial);
        if (opt->jo_exit_cb == NULL || *opt->jo_exit_cb == NUL)
+       {
            job->jv_exit_cb = NULL;
+           job->jv_exit_partial = NULL;
+       }
        else
+       {
            job->jv_exit_cb = vim_strsave(opt->jo_exit_cb);
+           job->jv_exit_partial = opt->jo_exit_partial;
+           if (job->jv_exit_partial != NULL)
+               ++job->jv_exit_partial->pt_refcount;
+       }
     }
 }
 
@@ -3721,7 +3792,8 @@ job_status(job_T *job)
            argv[1].v_type = VAR_NUMBER;
            argv[1].vval.v_number = job->jv_exitval;
            call_func(job->jv_exit_cb, (int)STRLEN(job->jv_exit_cb),
-                                &rettv, 2, argv, 0L, 0L, &dummy, TRUE, NULL);
+                          &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
+                          job->jv_exit_partial, NULL);
            clear_tv(&rettv);
            --job->jv_refcount;
        }
index e045a67530b55fbf7a51797cd01546fac3f71e07..3040e93d01a41d24bfd2d06bf852ef5245ad7d71 100644 (file)
@@ -452,8 +452,8 @@ static char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copy
 static char_u *string_quote(char_u *str, int function);
 static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate);
 static int find_internal_func(char_u *name);
-static char_u *deref_func_name(char_u *name, int *lenp, int no_autoload);
-static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict);
+static char_u *deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload);
+static int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict);
 static void emsg_funcname(char *ermsg, char_u *name);
 static int non_zero_arg(typval_T *argvars);
 
@@ -1675,7 +1675,7 @@ call_vim_function(
     rettv->v_type = VAR_UNKNOWN;               /* clear_tv() uses this */
     ret = call_func(func, (int)STRLEN(func), rettv, argc, argvars,
                    curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-                   &doesrange, TRUE, NULL);
+                   &doesrange, TRUE, NULL, NULL);
     if (safe)
     {
        --sandbox;
@@ -3091,6 +3091,7 @@ tv_op(typval_T *tv1, typval_T *tv2, char_u *op)
            case VAR_UNKNOWN:
            case VAR_DICT:
            case VAR_FUNC:
+           case VAR_PARTIAL:
            case VAR_SPECIAL:
            case VAR_JOB:
            case VAR_CHANNEL:
@@ -3456,6 +3457,7 @@ ex_call(exarg_T *eap)
     int                doesrange;
     int                failed = FALSE;
     funcdict_T fudi;
+    partial_T  *partial;
 
     if (eap->skip)
     {
@@ -3486,7 +3488,7 @@ ex_call(exarg_T *eap)
 
     /* If it is the name of a variable of type VAR_FUNC use its contents. */
     len = (int)STRLEN(tofree);
-    name = deref_func_name(tofree, &len, FALSE);
+    name = deref_func_name(tofree, &len, &partial, FALSE);
 
     /* Skip white space to allow ":call func ()".  Not good, but required for
      * backward compatibility. */
@@ -3525,7 +3527,7 @@ ex_call(exarg_T *eap)
        arg = startarg;
        if (get_func_tv(name, (int)STRLEN(name), &rettv, &arg,
                    eap->line1, eap->line2, &doesrange,
-                                           !eap->skip, fudi.fd_dict) == FAIL)
+                                  !eap->skip, partial, fudi.fd_dict) == FAIL)
        {
            failed = TRUE;
            break;
@@ -3870,6 +3872,7 @@ item_lock(typval_T *tv, int deep, int lock)
        case VAR_NUMBER:
        case VAR_STRING:
        case VAR_FUNC:
+       case VAR_PARTIAL:
        case VAR_FLOAT:
        case VAR_SPECIAL:
        case VAR_JOB:
@@ -4542,7 +4545,8 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
                }
            }
 
-           else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC)
+           else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC
+               || rettv->v_type == VAR_PARTIAL || var2.v_type == VAR_PARTIAL)
            {
                if (rettv->v_type != var2.v_type
                        || (type != TYPE_EQUAL && type != TYPE_NEQUAL))
@@ -4555,6 +4559,12 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
                    clear_tv(&var2);
                    return FAIL;
                }
+               else if (rettv->v_type == VAR_PARTIAL)
+               {
+                   /* Partials are only equal when identical. */
+                   n1 = rettv->vval.v_partial != NULL
+                             && rettv->vval.v_partial == var2.vval.v_partial;
+               }
                else
                {
                    /* Compare two Funcrefs for being equal or unequal. */
@@ -4564,9 +4574,9 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
                    else
                        n1 = STRCMP(rettv->vval.v_string,
                                                     var2.vval.v_string) == 0;
-                   if (type == TYPE_NEQUAL)
-                       n1 = !n1;
                }
+               if (type == TYPE_NEQUAL)
+                   n1 = !n1;
            }
 
 #ifdef FEAT_FLOAT
@@ -5230,14 +5240,16 @@ eval7(
        {
            if (**arg == '(')           /* recursive! */
            {
+               partial_T *partial;
+
                /* If "s" is the name of a variable of type VAR_FUNC
                 * use its contents. */
-               s = deref_func_name(s, &len, !evaluate);
+               s = deref_func_name(s, &len, &partial, !evaluate);
 
                /* Invoke the function. */
                ret = get_func_tv(s, len, rettv, arg,
                          curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-                         &len, evaluate, NULL);
+                         &len, evaluate, partial, NULL);
 
                /* If evaluate is FALSE rettv->v_type was not set in
                 * get_func_tv, but it's needed in handle_subscript() to parse
@@ -5359,6 +5371,7 @@ eval_index(
     switch (rettv->v_type)
     {
        case VAR_FUNC:
+       case VAR_PARTIAL:
            if (verbose)
                EMSG(_("E695: Cannot index a Funcref"));
            return FAIL;
@@ -5480,6 +5493,7 @@ eval_index(
        {
            case VAR_UNKNOWN:
            case VAR_FUNC:
+           case VAR_PARTIAL:
            case VAR_FLOAT:
            case VAR_SPECIAL:
            case VAR_JOB:
@@ -6218,6 +6232,10 @@ tv_equal(
                    && tv2->vval.v_string != NULL
                    && STRCMP(tv1->vval.v_string, tv2->vval.v_string) == 0);
 
+       case VAR_PARTIAL:
+           return tv1->vval.v_partial != NULL
+                   && tv1->vval.v_partial == tv2->vval.v_partial;
+
        case VAR_NUMBER:
            return tv1->vval.v_number == tv2->vval.v_number;
 
@@ -7793,6 +7811,12 @@ echo_string(
            r = tv->vval.v_string;
            break;
 
+       case VAR_PARTIAL:
+           *tofree = NULL;
+           /* TODO: arguments */
+           r = tv->vval.v_partial == NULL ? NULL : tv->vval.v_partial->pt_name;
+           break;
+
        case VAR_LIST:
            if (tv->vval.v_list == NULL)
            {
@@ -7878,6 +7902,10 @@ tv2string(
        case VAR_FUNC:
            *tofree = string_quote(tv->vval.v_string, TRUE);
            return *tofree;
+       case VAR_PARTIAL:
+           *tofree = string_quote(tv->vval.v_partial == NULL ? NULL
+                                        : tv->vval.v_partial->pt_name, TRUE);
+           return *tofree;
        case VAR_STRING:
            *tofree = string_quote(tv->vval.v_string, FALSE);
            return *tofree;
@@ -8146,7 +8174,7 @@ static struct fst
     {"foldtext",       0, 0, f_foldtext},
     {"foldtextresult", 1, 1, f_foldtextresult},
     {"foreground",     0, 0, f_foreground},
-    {"function",       1, 1, f_function},
+    {"function",       1, 3, f_function},
     {"garbagecollect", 0, 1, f_garbagecollect},
     {"get",            2, 3, f_get},
     {"getbufline",     2, 3, f_getbufline},
@@ -8524,13 +8552,16 @@ find_internal_func(
 /*
  * Check if "name" is a variable of type VAR_FUNC.  If so, return the function
  * name it contains, otherwise return "name".
+ * If "name" is of type VAR_PARTIAL also return "partial"
  */
     static char_u *
-deref_func_name(char_u *name, int *lenp, int no_autoload)
+deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload)
 {
     dictitem_T *v;
     int                cc;
 
+    *partial = NULL;
+
     cc = name[*lenp];
     name[*lenp] = NUL;
     v = find_var(name, NULL, no_autoload);
@@ -8546,6 +8577,18 @@ deref_func_name(char_u *name, int *lenp, int no_autoload)
        return v->di_tv.vval.v_string;
     }
 
+    if (v != NULL && v->di_tv.v_type == VAR_PARTIAL)
+    {
+       *partial = v->di_tv.vval.v_partial;
+       if (*partial == NULL)
+       {
+           *lenp = 0;
+           return (char_u *)"";        /* just in case */
+       }
+       *lenp = (int)STRLEN((*partial)->pt_name);
+       return (*partial)->pt_name;
+    }
+
     return name;
 }
 
@@ -8563,6 +8606,7 @@ get_func_tv(
     linenr_T   lastline,       /* last line of range */
     int                *doesrange,     /* return: function handled range */
     int                evaluate,
+    partial_T  *partial,       /* for extra arguments */
     dict_T     *selfdict)      /* Dictionary for "self" */
 {
     char_u     *argp;
@@ -8574,7 +8618,7 @@ get_func_tv(
      * Get the arguments.
      */
     argp = *arg;
-    while (argcount < MAX_FUNC_ARGS)
+    while (argcount < MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc))
     {
        argp = skipwhite(argp + 1);         /* skip the '(' or ',' */
        if (*argp == ')' || *argp == ',' || *argp == NUL)
@@ -8595,7 +8639,7 @@ get_func_tv(
 
     if (ret == OK)
        ret = call_func(name, len, rettv, argcount, argvars,
-                         firstline, lastline, doesrange, evaluate, selfdict);
+                firstline, lastline, doesrange, evaluate, partial, selfdict);
     else if (!aborting())
     {
        if (argcount == MAX_FUNC_ARGS)
@@ -8622,14 +8666,15 @@ call_func(
     char_u     *funcname,      /* name of the function */
     int                len,            /* length of "name" */
     typval_T   *rettv,         /* return value goes here */
-    int                argcount,       /* number of "argvars" */
-    typval_T   *argvars,       /* vars for arguments, must have "argcount"
+    int                argcount_in,    /* number of "argvars" */
+    typval_T   *argvars_in,    /* vars for arguments, must have "argcount"
                                   PLUS ONE elements! */
     linenr_T   firstline,      /* first line of range */
     linenr_T   lastline,       /* last line of range */
     int                *doesrange,     /* return: function handled range */
     int                evaluate,
-    dict_T     *selfdict)      /* Dictionary for "self" */
+    partial_T  *partial,       /* optional, can be NULL */
+    dict_T     *selfdict_in)   /* Dictionary for "self" */
 {
     int                ret = FAIL;
 #define ERROR_UNKNOWN  0
@@ -8639,6 +8684,7 @@ call_func(
 #define ERROR_DICT     4
 #define ERROR_NONE     5
 #define ERROR_OTHER    6
+#define ERROR_BOTH     7
     int                error = ERROR_NONE;
     int                i;
     int                llen;
@@ -8647,6 +8693,11 @@ call_func(
     char_u     fname_buf[FLEN_FIXED + 1];
     char_u     *fname;
     char_u     *name;
+    int                argcount = argcount_in;
+    typval_T   *argvars = argvars_in;
+    dict_T     *selfdict = selfdict_in;
+    typval_T   argv[MAX_FUNC_ARGS + 1]; /* used when "partial" is not NULL */
+    int                argv_clear = 0;
 
     /* Make a copy of the name, if it comes from a funcref variable it could
      * be changed or deleted in the called function. */
@@ -8698,6 +8749,27 @@ call_func(
 
     *doesrange = FALSE;
 
+    if (partial != NULL)
+    {
+       if (partial->pt_dict != NULL)
+       {
+           if (selfdict_in != NULL)
+               error = ERROR_BOTH;
+           selfdict = partial->pt_dict;
+       }
+       if (error == ERROR_NONE && partial->pt_argc > 0)
+       {
+           int     i;
+
+           for (argv_clear = 0; argv_clear < partial->pt_argc; ++argv_clear)
+               copy_tv(&partial->pt_argv[argv_clear], &argv[argv_clear]);
+           for (i = 0; i < argcount_in; ++i)
+               argv[i + argv_clear] = argvars_in[i];
+           argvars = argv;
+           argcount = partial->pt_argc + argcount_in;
+       }
+    }
+
 
     /* execute the function if no errors detected and executing */
     if (evaluate && error == ERROR_NONE)
@@ -8841,9 +8913,15 @@ call_func(
                    emsg_funcname(N_("E725: Calling dict function without Dictionary: %s"),
                                                                        name);
                    break;
+           case ERROR_BOTH:
+                   emsg_funcname(N_("E924: can't have both a \"self\" dict and a partial: %s"),
+                                                                       name);
+                   break;
        }
     }
 
+    while (argv_clear > 0)
+       clear_tv(&argv[--argv_clear]);
     if (fname != name && fname != fname_buf)
        vim_free(fname);
     vim_free(name);
@@ -9737,6 +9815,7 @@ f_byteidxcomp(typval_T *argvars, typval_T *rettv)
 func_call(
     char_u     *name,
     typval_T   *args,
+    partial_T  *partial,
     dict_T     *selfdict,
     typval_T   *rettv)
 {
@@ -9749,7 +9828,7 @@ func_call(
     for (item = args->vval.v_list->lv_first; item != NULL;
                                                         item = item->li_next)
     {
-       if (argc == MAX_FUNC_ARGS)
+       if (argc == MAX_FUNC_ARGS - (partial == NULL ? 0 : partial->pt_argc))
        {
            EMSG(_("E699: Too many arguments"));
            break;
@@ -9763,7 +9842,7 @@ func_call(
     if (item == NULL)
        r = call_func(name, (int)STRLEN(name), rettv, argc, argv,
                                 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-                                                     &dummy, TRUE, selfdict);
+                                            &dummy, TRUE, partial, selfdict);
 
     /* Free the arguments. */
     while (argc > 0)
@@ -9773,12 +9852,13 @@ func_call(
 }
 
 /*
- * "call(func, arglist)" function
+ * "call(func, arglist [, dict])" function
  */
     static void
 f_call(typval_T *argvars, typval_T *rettv)
 {
     char_u     *func;
+    partial_T   *partial = NULL;
     dict_T     *selfdict = NULL;
 
     if (argvars[1].v_type != VAR_LIST)
@@ -9791,6 +9871,11 @@ f_call(typval_T *argvars, typval_T *rettv)
 
     if (argvars[0].v_type == VAR_FUNC)
        func = argvars[0].vval.v_string;
+    else if (argvars[0].v_type == VAR_PARTIAL)
+    {
+       partial = argvars[0].vval.v_partial;
+       func = partial->pt_name;
+    }
     else
        func = get_tv_string(&argvars[0]);
     if (*func == NUL)
@@ -9806,7 +9891,7 @@ f_call(typval_T *argvars, typval_T *rettv)
        selfdict = argvars[2].vval.v_dict;
     }
 
-    (void)func_call(func, &argvars[1], selfdict, rettv);
+    (void)func_call(func, &argvars[1], partial, selfdict, rettv);
 }
 
 #ifdef FEAT_FLOAT
@@ -10627,6 +10712,9 @@ f_empty(typval_T *argvars, typval_T *rettv)
            n = argvars[0].vval.v_string == NULL
                                          || *argvars[0].vval.v_string == NUL;
            break;
+       case VAR_PARTIAL:
+           n = FALSE;
+           break;
        case VAR_NUMBER:
            n = argvars[0].vval.v_number == 0;
            break;
@@ -11688,6 +11776,7 @@ f_foreground(typval_T *argvars UNUSED, typval_T *rettv UNUSED)
 f_function(typval_T *argvars, typval_T *rettv)
 {
     char_u     *s;
+    char_u     *name;
 
     s = get_tv_string(&argvars[0]);
     if (s == NULL || *s == NUL || VIM_ISDIGIT(*s))
@@ -11707,20 +11796,120 @@ f_function(typval_T *argvars, typval_T *rettv)
             * would also work, but some plugins depend on the name being
             * printable text. */
            sprintf(sid_buf, "<SNR>%ld_", (long)current_SID);
-           rettv->vval.v_string =
-                           alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1));
-           if (rettv->vval.v_string != NULL)
+           name = alloc((int)(STRLEN(sid_buf) + STRLEN(s + off) + 1));
+           if (name != NULL)
+           {
+               STRCPY(name, sid_buf);
+               STRCAT(name, s + off);
+           }
+       }
+       else
+           name = vim_strsave(s);
+
+       if (argvars[1].v_type != VAR_UNKNOWN)
+       {
+           partial_T   *pt;
+           int         dict_idx = 0;
+           int         arg_idx = 0;
+
+           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 && (argvars[dict_idx].v_type != VAR_DICT
+                                    || argvars[dict_idx].vval.v_dict == NULL))
+           {
+               EMSG(_("E922: expected a dict"));
+               vim_free(name);
+               return;
+           }
+           if (arg_idx > 0 && (argvars[arg_idx].v_type != VAR_LIST
+                                    || argvars[arg_idx].vval.v_list == NULL))
            {
-               STRCPY(rettv->vval.v_string, sid_buf);
-               STRCAT(rettv->vval.v_string, s + off);
+               EMSG(_("E923: Second argument of function() must be a list or a dict"));
+               vim_free(name);
+               return;
            }
+
+           pt = (partial_T *)alloc_clear(sizeof(partial_T));
+           if (pt != NULL)
+           {
+               if (arg_idx > 0)
+               {
+                   list_T      *list = argvars[arg_idx].vval.v_list;
+                   listitem_T  *li;
+                   int         i = 0;
+
+                   pt->pt_argv = (typval_T *)alloc(
+                                            sizeof(typval_T) * list->lv_len);
+                   if (pt->pt_argv == NULL)
+                   {
+                       vim_free(pt);
+                       vim_free(name);
+                       return;
+                   }
+                   else
+                   {
+                       pt->pt_argc = list->lv_len;
+                       for (li = list->lv_first; li != NULL; li = li->li_next)
+                           copy_tv(&li->li_tv, &pt->pt_argv[i++]);
+                   }
+               }
+
+               if (dict_idx > 0)
+               {
+                   pt->pt_dict = argvars[dict_idx].vval.v_dict;
+                   ++pt->pt_dict->dv_refcount;
+               }
+
+               pt->pt_refcount = 1;
+               pt->pt_name = name;
+               func_ref(pt->pt_name);
+           }
+           rettv->v_type = VAR_PARTIAL;
+           rettv->vval.v_partial = pt;
        }
        else
-           rettv->vval.v_string = vim_strsave(s);
-       rettv->v_type = VAR_FUNC;
+       {
+           rettv->v_type = VAR_FUNC;
+           rettv->vval.v_string = name;
+           func_ref(name);
+       }
     }
 }
 
+    static void
+partial_free(partial_T *pt)
+{
+    int i;
+
+    for (i = 0; i < pt->pt_argc; ++i)
+       clear_tv(&pt->pt_argv[i]);
+    vim_free(pt->pt_argv);
+    func_unref(pt->pt_name);
+    vim_free(pt->pt_name);
+    vim_free(pt);
+}
+
+/*
+ * Unreference a closure: decrement the reference count and free it when it
+ * becomes zero.
+ */
+    void
+partial_unref(partial_T *pt)
+{
+    if (pt != NULL && --pt->pt_refcount <= 0)
+       partial_free(pt);
+}
+
 /*
  * "garbagecollect()" function
  */
@@ -14598,6 +14787,7 @@ f_len(typval_T *argvars, typval_T *rettv)
        case VAR_SPECIAL:
        case VAR_FLOAT:
        case VAR_FUNC:
+       case VAR_PARTIAL:
        case VAR_JOB:
        case VAR_CHANNEL:
            EMSG(_("E701: Invalid type for len()"));
@@ -18169,6 +18359,7 @@ typedef struct
     int                item_compare_float;
 #endif
     char_u     *item_compare_func;
+    partial_T  *item_compare_partial;
     dict_T     *item_compare_selfdict;
     int                item_compare_func_err;
     int                item_compare_keep_zero;
@@ -18278,6 +18469,8 @@ item_compare2(const void *s1, const void *s2)
     typval_T   rettv;
     typval_T   argv[3];
     int                dummy;
+    char_u     *func_name;
+    partial_T  *partial = sortinfo->item_compare_partial;
 
     /* shortcut after failure in previous call; compare all items equal */
     if (sortinfo->item_compare_func_err)
@@ -18286,16 +18479,20 @@ item_compare2(const void *s1, const void *s2)
     si1 = (sortItem_T *)s1;
     si2 = (sortItem_T *)s2;
 
+    if (partial == NULL)
+       func_name = sortinfo->item_compare_func;
+    else
+       func_name = partial->pt_name;
+
     /* Copy the values.  This is needed to be able to set v_lock to VAR_FIXED
      * in the copy without changing the original list items. */
     copy_tv(&si1->item->li_tv, &argv[0]);
     copy_tv(&si2->item->li_tv, &argv[1]);
 
     rettv.v_type = VAR_UNKNOWN;                /* clear_tv() uses this */
-    res = call_func(sortinfo->item_compare_func,
-                                (int)STRLEN(sortinfo->item_compare_func),
+    res = call_func(func_name, (int)STRLEN(func_name),
                                 &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
-                                sortinfo->item_compare_selfdict);
+                                partial, sortinfo->item_compare_selfdict);
     clear_tv(&argv[0]);
     clear_tv(&argv[1]);
 
@@ -18358,12 +18555,15 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
        info.item_compare_float = FALSE;
 #endif
        info.item_compare_func = NULL;
+       info.item_compare_partial = NULL;
        info.item_compare_selfdict = NULL;
        if (argvars[1].v_type != VAR_UNKNOWN)
        {
            /* optional second argument: {func} */
            if (argvars[1].v_type == VAR_FUNC)
                info.item_compare_func = argvars[1].vval.v_string;
+           else if (argvars[1].v_type == VAR_PARTIAL)
+               info.item_compare_partial = argvars[1].vval.v_partial;
            else
            {
                int         error = FALSE;
@@ -18443,7 +18643,8 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
            info.item_compare_func_err = FALSE;
            info.item_compare_keep_zero = FALSE;
            /* test the compare function */
-           if (info.item_compare_func != NULL
+           if ((info.item_compare_func != NULL
+                                        || info.item_compare_partial != NULL)
                    && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
                                                         == ITEM_COMPARE_FAIL)
                EMSG(_("E702: Sort compare function failed"));
@@ -18452,6 +18653,7 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
                /* Sort the array with item pointers. */
                qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
                    info.item_compare_func == NULL
+                                         && info.item_compare_partial == NULL
                                               ? item_compare : item_compare2);
 
                if (!info.item_compare_func_err)
@@ -18471,7 +18673,8 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
            /* f_uniq(): ptrs will be a stack of items to remove */
            info.item_compare_func_err = FALSE;
            info.item_compare_keep_zero = TRUE;
-           item_compare_func_ptr = info.item_compare_func
+           item_compare_func_ptr = info.item_compare_func != NULL
+                                         || info.item_compare_partial != NULL
                                               ? item_compare2 : item_compare;
 
            for (li = l->lv_first; li != NULL && li->li_next != NULL;
@@ -20045,6 +20248,7 @@ f_type(typval_T *argvars, typval_T *rettv)
             break;
        case VAR_JOB:     n = 8; break;
        case VAR_CHANNEL: n = 9; break;
+       case VAR_PARTIAL: n = 10; break;
        case VAR_UNKNOWN:
             EMSG2(_(e_intern2), "f_type(UNKNOWN)");
             n = -1;
@@ -21295,11 +21499,13 @@ handle_subscript(
     char_u     *s;
     int                len;
     typval_T   functv;
+    partial_T  *pt = NULL;
 
     while (ret == OK
            && (**arg == '['
                || (**arg == '.' && rettv->v_type == VAR_DICT)
-               || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC)))
+               || (**arg == '(' && (!evaluate || rettv->v_type == VAR_FUNC
+                                           || rettv->v_type == VAR_PARTIAL)))
            && !vim_iswhite(*(*arg - 1)))
     {
        if (**arg == '(')
@@ -21311,13 +21517,19 @@ handle_subscript(
                rettv->v_type = VAR_UNKNOWN;
 
                /* Invoke the function.  Recursive! */
-               s = functv.vval.v_string;
+               if (rettv->v_type == VAR_PARTIAL)
+               {
+                   pt = functv.vval.v_partial;
+                   s = pt->pt_name;
+               }
+               else
+                   s = functv.vval.v_string;
            }
            else
                s = (char_u *)"";
            ret = get_func_tv(s, (int)STRLEN(s), rettv, arg,
                        curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-                       &len, evaluate, selfdict);
+                       &len, evaluate, pt, selfdict);
 
            /* Clear the funcref afterwards, so that deleting it while
             * evaluating the arguments is possible (see test55). */
@@ -21405,6 +21617,9 @@ free_tv(typval_T *varp)
            case VAR_STRING:
                vim_free(varp->vval.v_string);
                break;
+           case VAR_PARTIAL:
+               partial_unref(varp->vval.v_partial);
+               break;
            case VAR_LIST:
                list_unref(varp->vval.v_list);
                break;
@@ -21448,6 +21663,10 @@ clear_tv(typval_T *varp)
                vim_free(varp->vval.v_string);
                varp->vval.v_string = NULL;
                break;
+           case VAR_PARTIAL:
+               partial_unref(varp->vval.v_partial);
+               varp->vval.v_partial = NULL;
+               break;
            case VAR_LIST:
                list_unref(varp->vval.v_list);
                varp->vval.v_list = NULL;
@@ -21524,6 +21743,7 @@ get_tv_number_chk(typval_T *varp, int *denote)
            break;
 #endif
        case VAR_FUNC:
+       case VAR_PARTIAL:
            EMSG(_("E703: Using a Funcref as a Number"));
            break;
        case VAR_STRING:
@@ -21572,6 +21792,7 @@ get_tv_float(typval_T *varp)
        case VAR_FLOAT:
            return varp->vval.v_float;
        case VAR_FUNC:
+       case VAR_PARTIAL:
            EMSG(_("E891: Using a Funcref as a Float"));
            break;
        case VAR_STRING:
@@ -21688,6 +21909,7 @@ get_tv_string_buf_chk(typval_T *varp, char_u *buf)
            sprintf((char *)buf, "%ld", (long)varp->vval.v_number);
            return buf;
        case VAR_FUNC:
+       case VAR_PARTIAL:
            EMSG(_("E729: using Funcref as a String"));
            break;
        case VAR_LIST:
@@ -22087,7 +22309,7 @@ list_one_var_a(
     msg_advance(22);
     if (type == VAR_NUMBER)
        msg_putchar('#');
-    else if (type == VAR_FUNC)
+    else if (type == VAR_FUNC || type == VAR_PARTIAL)
        msg_putchar('*');
     else if (type == VAR_LIST)
     {
@@ -22106,7 +22328,7 @@ list_one_var_a(
 
     msg_outtrans(string);
 
-    if (type == VAR_FUNC)
+    if (type == VAR_FUNC || type == VAR_PARTIAL)
        msg_puts((char_u *)"()");
     if (*first)
     {
@@ -22138,7 +22360,8 @@ set_var(
     }
     v = find_var_in_ht(ht, 0, varname, TRUE);
 
-    if (tv->v_type == VAR_FUNC && var_check_func_name(name, v == NULL))
+    if ((tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
+                                     && var_check_func_name(name, v == NULL))
        return;
 
     if (v != NULL)
@@ -22383,6 +22606,15 @@ copy_tv(typval_T *from, typval_T *to)
                    func_ref(to->vval.v_string);
            }
            break;
+       case VAR_PARTIAL:
+           if (from->vval.v_partial == NULL)
+               to->vval.v_partial = NULL;
+           else
+           {
+               to->vval.v_partial = from->vval.v_partial;
+               ++to->vval.v_partial->pt_refcount;
+           }
+           break;
        case VAR_LIST:
            if (from->vval.v_list == NULL)
                to->vval.v_list = NULL;
@@ -22437,6 +22669,7 @@ item_copy(
        case VAR_FLOAT:
        case VAR_STRING:
        case VAR_FUNC:
+       case VAR_PARTIAL:
        case VAR_SPECIAL:
        case VAR_JOB:
        case VAR_CHANNEL:
@@ -23415,6 +23648,7 @@ trans_function_name(
     char_u     sid_buf[20];
     int                len;
     lval_T     lv;
+    partial_T  *partial;
 
     if (fdp != NULL)
        vim_memset(fdp, 0, sizeof(funcdict_T));
@@ -23499,14 +23733,15 @@ trans_function_name(
     if (lv.ll_exp_name != NULL)
     {
        len = (int)STRLEN(lv.ll_exp_name);
-       name = deref_func_name(lv.ll_exp_name, &len, flags & TFN_NO_AUTOLOAD);
+       name = deref_func_name(lv.ll_exp_name, &len, &partial,
+                                                    flags & TFN_NO_AUTOLOAD);
        if (name == lv.ll_exp_name)
            name = NULL;
     }
     else
     {
        len = (int)(end - *pp);
-       name = deref_func_name(*pp, &len, flags & TFN_NO_AUTOLOAD);
+       name = deref_func_name(*pp, &len, &partial, flags & TFN_NO_AUTOLOAD);
        if (name == *pp)
            name = NULL;
     }
@@ -25111,6 +25346,7 @@ write_viminfo_varlist(FILE *fp)
 
                    case VAR_UNKNOWN:
                    case VAR_FUNC:
+                   case VAR_PARTIAL:
                    case VAR_JOB:
                    case VAR_CHANNEL:
                                     continue;
index e916e6aa8dcfebf1b32f733148c0819ec0396746..8070bd6e0eea9278bb7504ab9e42290e676cf8f0 100644 (file)
@@ -2944,7 +2944,7 @@ FunctionCall(FunctionObject *self, PyObject *argsObject, PyObject *kwargs)
     Python_Lock_Vim();
 
     VimTryStart();
-    error = func_call(name, &args, selfdict, &rettv);
+    error = func_call(name, &args, NULL, selfdict, &rettv);
 
     Python_Release_Vim();
     Py_END_ALLOW_THREADS
index 56b0ee597ace7b1fb75314ca2a62d55c4e4fa35c..fa3fc88a86604433d8fec29cb8f4d3a77a6bde6a 100644 (file)
@@ -1561,6 +1561,7 @@ do_pyeval (char_u *str, typval_T *rettv)
        case VAR_DICT: ++rettv->vval.v_dict->dv_refcount; break;
        case VAR_LIST: ++rettv->vval.v_list->lv_refcount; break;
        case VAR_FUNC: func_ref(rettv->vval.v_string);    break;
+       case VAR_PARTIAL: ++rettv->vval.v_partial->pt_refcount; break;
        case VAR_UNKNOWN:
            rettv->v_type = VAR_NUMBER;
            rettv->vval.v_number = 0;
index 84302bd636961cb31899dc766893d96e70332c5c..5cf508fc71247c9b32832aebc094095183ac8478 100644 (file)
@@ -1654,6 +1654,7 @@ do_py3eval (char_u *str, typval_T *rettv)
        case VAR_DICT: ++rettv->vval.v_dict->dv_refcount; break;
        case VAR_LIST: ++rettv->vval.v_list->lv_refcount; break;
        case VAR_FUNC: func_ref(rettv->vval.v_string);    break;
+       case VAR_PARTIAL: ++rettv->vval.v_partial->pt_refcount; break;
        case VAR_UNKNOWN:
            rettv->v_type = VAR_NUMBER;
            rettv->vval.v_number = 0;
index 34cf188a1df99056e91aadf175845a66e6976e2c..cd80a761ec5fd2abbe9891026bb2f5ca6a0a4b1f 100644 (file)
@@ -212,6 +212,7 @@ json_encode_item(garray_T *gap, typval_T *val, int copyID, int options)
            break;
 
        case VAR_FUNC:
+       case VAR_PARTIAL:
        case VAR_JOB:
        case VAR_CHANNEL:
            /* no JSON equivalent TODO: better error */
index 7035f26e24b277ab4ebef85c931a63d15aae914a..dee95c4ed2c873bbadf383de3a069eb26a9f2c4a 100644 (file)
@@ -83,10 +83,11 @@ long get_dict_number(dict_T *d, char_u *key);
 int string2float(char_u *text, float_T *value);
 char_u *get_function_name(expand_T *xp, int idx);
 char_u *get_expr_name(expand_T *xp, int idx);
-int call_func(char_u *funcname, int len, typval_T *rettv, int argcount, typval_T *argvars, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, dict_T *selfdict);
+int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, linenr_T firstline, linenr_T lastline, int *doesrange, int evaluate, partial_T *partial, dict_T *selfdict_in);
 buf_T *buflist_find_by_name(char_u *name, int curtab_only);
-int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv);
+int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv);
 void dict_extend(dict_T *d1, dict_T *d2, char_u *action);
+void partial_unref(partial_T *pt);
 void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv);
 float_T vim_round(float_T f);
 long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit);
index 4aa7415f0fbe714caed668f2e0f1f72f9e3d674d..04360e29133984e6b8094ffb81c5fedbd9f302e9 100644 (file)
@@ -1110,6 +1110,7 @@ typedef double    float_T;
 
 typedef struct listvar_S list_T;
 typedef struct dictvar_S dict_T;
+typedef struct partial_S partial_T;
 
 typedef struct jobvar_S job_T;
 typedef struct readq_S readq_T;
@@ -1123,6 +1124,7 @@ typedef enum
     VAR_NUMBER,         /* "v_number" is used */
     VAR_STRING,         /* "v_string" is used */
     VAR_FUNC,   /* "v_string" is function name */
+    VAR_PARTIAL, /* "v_partial" is used */
     VAR_LIST,   /* "v_list" is used */
     VAR_DICT,   /* "v_dict" is used */
     VAR_FLOAT,  /* "v_float" is used */
@@ -1147,6 +1149,7 @@ typedef struct
        char_u          *v_string;      /* string value (can be NULL!) */
        list_T          *v_list;        /* list value (can be NULL!) */
        dict_T          *v_dict;        /* dict value (can be NULL!) */
+       partial_T       *v_partial;     /* closure: function with args */
 #ifdef FEAT_JOB_CHANNEL
        job_T           *v_job;         /* job value (can be NULL!) */
        channel_T       *v_channel;     /* channel value (can be NULL!) */
@@ -1239,6 +1242,15 @@ struct dictvar_S
     dict_T     *dv_used_prev;  /* previous dict in used dicts list */
 };
 
+struct partial_S
+{
+    int                pt_refcount;    /* reference count */
+    char_u     *pt_name;       /* function name */
+    int                pt_argc;        /* number of arguments */
+    typval_T   *pt_argv;       /* arguments in allocated array */
+    dict_T     *pt_dict;       /* dict for "self" */
+};
+
 typedef enum
 {
     JOB_FAILED,
@@ -1264,6 +1276,7 @@ struct jobvar_S
     char_u     *jv_stoponexit; /* allocated */
     int                jv_exitval;
     char_u     *jv_exit_cb;    /* allocated */
+    partial_T  *jv_exit_partial;
 
     buf_T      *jv_in_buf;     /* buffer from "in-name" */
 
@@ -1291,6 +1304,7 @@ struct jsonq_S
 struct cbq_S
 {
     char_u     *cq_callback;
+    partial_T  *cq_partial;
     int                cq_seq_nr;
     cbq_T      *cq_next;
     cbq_T      *cq_prev;
@@ -1346,6 +1360,7 @@ typedef struct {
 
     cbq_T      ch_cb_head;     /* dummy node for per-request callbacks */
     char_u     *ch_callback;   /* call when a msg is not handled */
+    partial_T  *ch_partial;
 
     buf_T      *ch_buffer;     /* buffer to read from or write to */
     linenr_T   ch_buf_top;     /* next line to send */
@@ -1371,7 +1386,9 @@ struct channel_S {
                                 * closed */
 
     char_u     *ch_callback;   /* call when any msg is not handled */
+    partial_T  *ch_partial;
     char_u     *ch_close_cb;   /* call when channel is closed */
+    partial_T  *ch_close_partial;
 
     job_T      *ch_job;        /* Job that uses this channel; this does not
                                 * count as a reference to avoid a circular
@@ -1447,9 +1464,15 @@ typedef struct
     linenr_T   jo_in_bot;
 
     char_u     *jo_callback;   /* not allocated! */
+    partial_T  *jo_partial;    /* not referenced! */
     char_u     *jo_out_cb;     /* not allocated! */
+    partial_T  *jo_out_partial; /* not referenced! */
     char_u     *jo_err_cb;     /* not allocated! */
+    partial_T  *jo_err_partial; /* not referenced! */
     char_u     *jo_close_cb;   /* not allocated! */
+    partial_T  *jo_close_partial; /* not referenced! */
+    char_u     *jo_exit_cb;    /* not allocated! */
+    partial_T  *jo_exit_partial; /* not referenced! */
     int                jo_waittime;
     int                jo_timeout;
     int                jo_out_timeout;
@@ -1459,7 +1482,6 @@ typedef struct
     char_u     jo_soe_buf[NUMBUFLEN];
     char_u     *jo_stoponexit;
     char_u     jo_ecb_buf[NUMBUFLEN];
-    char_u     *jo_exit_cb;
 } jobopt_T;
 
 
index a1d819d824ade3d08db031cc18040ab233f13977..55502bae6c80bcdbb82a3b34964c3079fa61d912 100644 (file)
@@ -12,6 +12,7 @@ source test_glob2regpat.vim
 source test_join.vim
 source test_lispwords.vim
 source test_menu.vim
+source test_partial.vim
 source test_reltime.vim
 source test_searchpos.vim
 source test_set.vim
diff --git a/src/testdir/test_partial.vim b/src/testdir/test_partial.vim
new file mode 100644 (file)
index 0000000..061f839
--- /dev/null
@@ -0,0 +1,43 @@
+" Test binding arguments to a Funcref.
+
+func MyFunc(arg1, arg2, arg3)
+  return a:arg1 . '/' . a:arg2 . '/' . a:arg3
+endfunc
+
+func MySort(up, one, two)
+  if a:one == a:two
+    return 0
+  endif
+  if a:up
+    return a:one > a:two
+  endif
+  return a:one < a:two
+endfunc
+
+func Test_partial_args()
+  let Cb = function('MyFunc', ["foo", "bar"])
+  call assert_equal("foo/bar/xxx", Cb("xxx"))
+  call assert_equal("foo/bar/yyy", call(Cb, ["yyy"]))
+
+  let Sort = function('MySort', [1])
+  call assert_equal([1, 2, 3], sort([3, 1, 2], Sort))
+  let Sort = function('MySort', [0])
+  call assert_equal([3, 2, 1], sort([3, 1, 2], Sort))
+endfunc
+
+func MyDictFunc(arg1, arg2) dict
+  return self.name . '/' . a:arg1 . '/' . a:arg2
+endfunc
+
+func Test_partial_dict()
+  let dict = {'name': 'hello'}
+  let Cb = function('MyDictFunc', ["foo", "bar"], dict)
+  call assert_equal("hello/foo/bar", Cb())
+  call assert_fails('Cb("xxx")', 'E492:')
+  let Cb = function('MyDictFunc', ["foo"], dict)
+  call assert_equal("hello/foo/xxx", Cb("xxx"))
+  call assert_fails('Cb()', 'E492:')
+  let Cb = function('MyDictFunc', dict)
+  call assert_equal("hello/xxx/yyy", Cb("xxx", "yyy"))
+  call assert_fails('Cb()', 'E492:')
+endfunc
index ac23c50fe44e6655d8229a536c5510c9318fa749..eb42b32792a616453b96b126a0a98d85d66d4492 100644 (file)
@@ -743,6 +743,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1559,
 /**/
     1558,
 /**/