mirror of
https://github.com/neovim/neovim.git
synced 2025-11-25 11:40:40 +00:00
Merge pull request #5529 from brcolow/vim-7.4.1559
Port partial patches from vim
This commit is contained in:
@@ -49,6 +49,9 @@ String A NUL terminated string of 8-bit unsigned characters (bytes).
|
|||||||
|
|
||||||
Funcref A reference to a function |Funcref|.
|
Funcref A reference to a function |Funcref|.
|
||||||
Example: function("strlen")
|
Example: function("strlen")
|
||||||
|
It can be bound to a dictionary and arguments, it then works
|
||||||
|
like a Partial.
|
||||||
|
Example: function("Callback", [arg], myDict)
|
||||||
|
|
||||||
List An ordered sequence of items |List|.
|
List An ordered sequence of items |List|.
|
||||||
Example: [1, 2, ['a', 'b']]
|
Example: [1, 2, ['a', 'b']]
|
||||||
@@ -139,6 +142,40 @@ The name of the referenced function can be obtained with |string()|. >
|
|||||||
You can use |call()| to invoke a Funcref and use a list variable for the
|
You can use |call()| to invoke a Funcref and use a list variable for the
|
||||||
arguments: >
|
arguments: >
|
||||||
:let r = call(Fn, mylist)
|
:let r = call(Fn, mylist)
|
||||||
|
<
|
||||||
|
*Partial*
|
||||||
|
A Funcref optionally binds a Dictionary and/or arguments. This is also called
|
||||||
|
a Partial. This is created by passing the Dictionary and/or arguments to
|
||||||
|
function(). When calling the function the Dictionary and/or arguments will be
|
||||||
|
passed to the function. Example: >
|
||||||
|
|
||||||
|
let Cb = function('Callback', ['foo'], myDict)
|
||||||
|
call Cb()
|
||||||
|
|
||||||
|
This will invoke the function as if using: >
|
||||||
|
call myDict.Callback('foo')
|
||||||
|
|
||||||
|
Note that binding a function to a Dictionary also happens when the function is
|
||||||
|
a member of the Dictionary: >
|
||||||
|
|
||||||
|
let myDict.myFunction = MyFunction
|
||||||
|
call myDict.myFunction()
|
||||||
|
|
||||||
|
Here MyFunction() will get myDict passed as "self". This happens when the
|
||||||
|
"myFunction" member is accessed. When assigning "myFunction" to otherDict
|
||||||
|
and calling it, it will be bound to otherDict: >
|
||||||
|
|
||||||
|
let otherDict.myFunction = myDict.myFunction
|
||||||
|
call otherDict.myFunction()
|
||||||
|
|
||||||
|
Now "self" will be "otherDict". But when the dictionary was bound explicitly
|
||||||
|
this won't happen: >
|
||||||
|
|
||||||
|
let myDict.myFunction = function(MyFunction, myDict)
|
||||||
|
let otherDict.myFunction = myDict.myFunction
|
||||||
|
call otherDict.myFunction()
|
||||||
|
|
||||||
|
Here "self" will be "myDict", because it was bound explitly.
|
||||||
|
|
||||||
|
|
||||||
1.3 Lists ~
|
1.3 Lists ~
|
||||||
@@ -1913,10 +1950,12 @@ foldlevel({lnum}) Number fold level at {lnum}
|
|||||||
foldtext() String line displayed for closed fold
|
foldtext() String line displayed for closed fold
|
||||||
foldtextresult({lnum}) String text for closed fold at {lnum}
|
foldtextresult({lnum}) String text for closed fold at {lnum}
|
||||||
foreground() Number bring the Vim window to the foreground
|
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
|
garbagecollect([{atexit}]) none free memory, breaking cyclic references
|
||||||
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
|
get({list}, {idx} [, {def}]) any get item {idx} from {list} or {def}
|
||||||
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
|
get({dict}, {key} [, {def}]) any get item {key} from {dict} or {def}
|
||||||
|
get({func}, {what}) any get property of funcref/partial {func}
|
||||||
getbufline({expr}, {lnum} [, {end}])
|
getbufline({expr}, {lnum} [, {end}])
|
||||||
List lines {lnum} to {end} of buffer {expr}
|
List lines {lnum} to {end} of buffer {expr}
|
||||||
getbufvar({expr}, {varname} [, {def}])
|
getbufvar({expr}, {varname} [, {def}])
|
||||||
@@ -3483,10 +3522,46 @@ foreground() Move the Vim window to the foreground. Useful when sent from
|
|||||||
{only in the Win32 GUI and console version}
|
{only in the Win32 GUI and console version}
|
||||||
|
|
||||||
|
|
||||||
function({name}) *function()* *E700*
|
*function()* *E700* *E922* *E929*
|
||||||
|
function({name} [, {arglist}] [, {dict}])
|
||||||
Return a |Funcref| variable that refers to function {name}.
|
Return a |Funcref| variable that refers to function {name}.
|
||||||
{name} can be a user defined function or an internal function.
|
{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()*
|
garbagecollect([{atexit}]) *garbagecollect()*
|
||||||
Cleanup unused |Lists| and |Dictionaries| that have circular
|
Cleanup unused |Lists| and |Dictionaries| that have circular
|
||||||
@@ -3510,6 +3585,13 @@ get({dict}, {key} [, {default}])
|
|||||||
Get item with key {key} from |Dictionary| {dict}. When this
|
Get item with key {key} from |Dictionary| {dict}. When this
|
||||||
item is not available return {default}. Return zero when
|
item is not available return {default}. Return zero when
|
||||||
{default} is omitted.
|
{default} is omitted.
|
||||||
|
get({func}, {what})
|
||||||
|
Get item {what} from Funcref {func}. Possible values for
|
||||||
|
{what} are:
|
||||||
|
'name' The function name
|
||||||
|
'func' The function
|
||||||
|
'dict' The dictionary
|
||||||
|
'args' The list with arguments
|
||||||
|
|
||||||
*getbufline()*
|
*getbufline()*
|
||||||
getbufline({expr}, {lnum} [, {end}])
|
getbufline({expr}, {lnum} [, {end}])
|
||||||
|
|||||||
@@ -235,7 +235,7 @@ Additional differences:
|
|||||||
These legacy Vim features may be implemented in the future, but they are not
|
These legacy Vim features may be implemented in the future, but they are not
|
||||||
planned for the current milestone.
|
planned for the current milestone.
|
||||||
|
|
||||||
- vim.bindeval() (new feature in Vim 7.4 Python interface)
|
- |if_py|: vim.bindeval() and vim.Function() are not supported
|
||||||
- |if_lua|
|
- |if_lua|
|
||||||
- |if_perl|
|
- |if_perl|
|
||||||
- |if_mzscheme|
|
- |if_mzscheme|
|
||||||
|
|||||||
@@ -360,6 +360,9 @@ void set_option_to(void *to, int type, String name, Object value, Error *err)
|
|||||||
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
#define TYPVAL_ENCODE_CONV_FUNC(fun) \
|
||||||
TYPVAL_ENCODE_CONV_NIL()
|
TYPVAL_ENCODE_CONV_NIL()
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
|
||||||
|
TYPVAL_ENCODE_CONV_NIL()
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
||||||
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
|
kv_push(edata->stack, ARRAY_OBJ(((Array) { .capacity = 0, .size = 0 })))
|
||||||
|
|
||||||
@@ -482,6 +485,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, object, EncodedData *const, edata)
|
|||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||||
|
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
@@ -653,7 +657,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
|
|||||||
if (!object_to_vim(item, &li->li_tv, err)) {
|
if (!object_to_vim(item, &li->li_tv, err)) {
|
||||||
// cleanup
|
// cleanup
|
||||||
listitem_free(li);
|
listitem_free(li);
|
||||||
list_free(list, true);
|
list_free(list);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -677,7 +681,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
|
|||||||
api_set_error(err, Validation,
|
api_set_error(err, Validation,
|
||||||
_("Empty dictionary keys aren't allowed"));
|
_("Empty dictionary keys aren't allowed"));
|
||||||
// cleanup
|
// cleanup
|
||||||
dict_free(dict, true);
|
dict_free(dict);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -686,7 +690,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
|
|||||||
if (!object_to_vim(item.value, &di->di_tv, err)) {
|
if (!object_to_vim(item.value, &di->di_tv, err)) {
|
||||||
// cleanup
|
// cleanup
|
||||||
dictitem_free(di);
|
dictitem_free(di);
|
||||||
dict_free(dict, true);
|
dict_free(dict);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -225,7 +225,7 @@ Object nvim_call_function(String fname, Array args, Error *err)
|
|||||||
&rettv, (int) args.size, vim_args,
|
&rettv, (int) args.size, vim_args,
|
||||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
||||||
true,
|
true,
|
||||||
NULL);
|
NULL, NULL);
|
||||||
if (r == FAIL) {
|
if (r == FAIL) {
|
||||||
api_set_error(err, Exception, _("Error calling function."));
|
api_set_error(err, Exception, _("Error calling function."));
|
||||||
}
|
}
|
||||||
|
|||||||
1381
src/nvim/eval.c
1381
src/nvim/eval.c
File diff suppressed because it is too large
Load Diff
@@ -103,7 +103,7 @@ return {
|
|||||||
foldtext={},
|
foldtext={},
|
||||||
foldtextresult={args=1},
|
foldtextresult={args=1},
|
||||||
foreground={},
|
foreground={},
|
||||||
['function']={args=1},
|
['function']={args={1, 3}},
|
||||||
garbagecollect={args={0, 1}},
|
garbagecollect={args={0, 1}},
|
||||||
get={args={2, 3}},
|
get={args={2, 3}},
|
||||||
getbufline={args={2, 3}},
|
getbufline={args={2, 3}},
|
||||||
|
|||||||
@@ -315,6 +315,60 @@ int encode_read_from_list(ListReaderState *const state, char *const buf,
|
|||||||
ga_append(gap, ')'); \
|
ga_append(gap, ')'); \
|
||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
|
||||||
|
do { \
|
||||||
|
int i; \
|
||||||
|
ga_concat(gap, "function("); \
|
||||||
|
if (&pt->pt_name != NULL) { \
|
||||||
|
size_t len; \
|
||||||
|
char_u *p; \
|
||||||
|
len = 3; \
|
||||||
|
len += STRLEN(pt->pt_name); \
|
||||||
|
for (p = pt->pt_name; *p != NUL; mb_ptr_adv(p)) { \
|
||||||
|
if (*p == '\'') { \
|
||||||
|
len++; \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
char_u *r, *s; \
|
||||||
|
s = r = xmalloc(len); \
|
||||||
|
if (r != NULL) { \
|
||||||
|
*r++ = '\''; \
|
||||||
|
for (p = pt->pt_name; *p != NUL; ) { \
|
||||||
|
if (*p == '\'') { \
|
||||||
|
*r++ = '\''; \
|
||||||
|
} \
|
||||||
|
MB_COPY_CHAR(p, r); \
|
||||||
|
} \
|
||||||
|
*r++ = '\''; \
|
||||||
|
*r++ = NUL; \
|
||||||
|
} \
|
||||||
|
ga_concat(gap, s); \
|
||||||
|
xfree(s); \
|
||||||
|
} \
|
||||||
|
if (pt->pt_argc > 0) { \
|
||||||
|
ga_concat(gap, ", ["); \
|
||||||
|
for (i = 0; i < pt->pt_argc; i++) { \
|
||||||
|
if (i > 0) { \
|
||||||
|
ga_concat(gap, ", "); \
|
||||||
|
} \
|
||||||
|
char *tofree = encode_tv2string(&pt->pt_argv[i], NULL); \
|
||||||
|
ga_concat(gap, tofree); \
|
||||||
|
xfree(tofree); \
|
||||||
|
} \
|
||||||
|
ga_append(gap, ']'); \
|
||||||
|
} \
|
||||||
|
if (pt->pt_dict != NULL) { \
|
||||||
|
typval_T dtv; \
|
||||||
|
ga_concat(gap, ", "); \
|
||||||
|
dtv.v_type = VAR_DICT; \
|
||||||
|
dtv.vval.v_dict = pt->pt_dict; \
|
||||||
|
char *tofree = encode_tv2string(&dtv, NULL); \
|
||||||
|
ga_concat(gap, tofree); \
|
||||||
|
xfree(tofree); \
|
||||||
|
} \
|
||||||
|
ga_append(gap, ')'); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
||||||
ga_concat(gap, "[]")
|
ga_concat(gap, "[]")
|
||||||
|
|
||||||
@@ -661,6 +715,12 @@ static inline int convert_to_json_string(garray_T *const gap,
|
|||||||
"attempt to dump function reference"), \
|
"attempt to dump function reference"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
|
|
||||||
|
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||||
|
#define TYPVAL_ENCODE_CONV_PARTIAL(pt) \
|
||||||
|
return conv_error(_("E474: Error while dumping %s, %s: " \
|
||||||
|
"attempt to dump partial"), \
|
||||||
|
mpstack, objname)
|
||||||
|
|
||||||
/// Check whether given key can be used in json_encode()
|
/// Check whether given key can be used in json_encode()
|
||||||
///
|
///
|
||||||
/// @param[in] tv Key to check.
|
/// @param[in] tv Key to check.
|
||||||
@@ -718,6 +778,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(static, json, garray_T *const, gap)
|
|||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||||
|
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
@@ -846,6 +907,11 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
|||||||
"attempt to dump function reference"), \
|
"attempt to dump function reference"), \
|
||||||
mpstack, objname)
|
mpstack, objname)
|
||||||
|
|
||||||
|
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
|
||||||
|
return conv_error(_("E5004: Error while dumping %s, %s: " \
|
||||||
|
"attempt to dump partial"), \
|
||||||
|
mpstack, objname)
|
||||||
|
|
||||||
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
||||||
msgpack_pack_array(packer, 0)
|
msgpack_pack_array(packer, 0)
|
||||||
|
|
||||||
@@ -902,6 +968,7 @@ TYPVAL_ENCODE_DEFINE_CONV_FUNCTIONS(, msgpack, msgpack_packer *const, packer)
|
|||||||
#undef TYPVAL_ENCODE_CONV_NUMBER
|
#undef TYPVAL_ENCODE_CONV_NUMBER
|
||||||
#undef TYPVAL_ENCODE_CONV_FLOAT
|
#undef TYPVAL_ENCODE_CONV_FLOAT
|
||||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||||
|
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||||
|
|||||||
@@ -69,6 +69,11 @@
|
|||||||
///
|
///
|
||||||
/// @param fun Function name.
|
/// @param fun Function name.
|
||||||
|
|
||||||
|
/// @def TYPVAL_ENCODE_CONV_PARTIAL
|
||||||
|
/// @brief Macros used to convert a partial
|
||||||
|
///
|
||||||
|
/// @param pt Partial name.
|
||||||
|
|
||||||
/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
|
/// @def TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||||
/// @brief Macros used to convert an empty list
|
/// @brief Macros used to convert an empty list
|
||||||
///
|
///
|
||||||
@@ -248,6 +253,10 @@ static int name##_convert_one_value(firstargtype firstargname, \
|
|||||||
TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \
|
TYPVAL_ENCODE_CONV_FUNC(tv->vval.v_string); \
|
||||||
break; \
|
break; \
|
||||||
} \
|
} \
|
||||||
|
case VAR_PARTIAL: { \
|
||||||
|
TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \
|
||||||
|
break; \
|
||||||
|
} \
|
||||||
case VAR_LIST: { \
|
case VAR_LIST: { \
|
||||||
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
|
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
|
||||||
TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
|
TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ typedef double float_T;
|
|||||||
|
|
||||||
typedef struct listvar_S list_T;
|
typedef struct listvar_S list_T;
|
||||||
typedef struct dictvar_S dict_T;
|
typedef struct dictvar_S dict_T;
|
||||||
|
typedef struct partial_S partial_T;
|
||||||
|
|
||||||
/// Special variable values
|
/// Special variable values
|
||||||
typedef enum {
|
typedef enum {
|
||||||
@@ -35,12 +36,13 @@ typedef enum {
|
|||||||
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
|
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
|
||||||
VAR_NUMBER, ///< Number, .v_number is used.
|
VAR_NUMBER, ///< Number, .v_number is used.
|
||||||
VAR_STRING, ///< String, .v_string is used.
|
VAR_STRING, ///< String, .v_string is used.
|
||||||
VAR_FUNC, ///< Function referene, .v_string is used for function name.
|
VAR_FUNC, ///< Function reference, .v_string is used as function name.
|
||||||
VAR_LIST, ///< List, .v_list is used.
|
VAR_LIST, ///< List, .v_list is used.
|
||||||
VAR_DICT, ///< Dictionary, .v_dict is used.
|
VAR_DICT, ///< Dictionary, .v_dict is used.
|
||||||
VAR_FLOAT, ///< Floating-point value, .v_float is used.
|
VAR_FLOAT, ///< Floating-point value, .v_float is used.
|
||||||
VAR_SPECIAL, ///< Special value (true, false, null), .v_special
|
VAR_SPECIAL, ///< Special value (true, false, null), .v_special
|
||||||
///< is used.
|
///< is used.
|
||||||
|
VAR_PARTIAL, ///< Partial, .v_partial is used.
|
||||||
} VarType;
|
} VarType;
|
||||||
|
|
||||||
/// Structure that holds an internal variable value
|
/// Structure that holds an internal variable value
|
||||||
@@ -54,6 +56,7 @@ typedef struct {
|
|||||||
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
|
char_u *v_string; ///< String, for VAR_STRING and VAR_FUNC, can be NULL.
|
||||||
list_T *v_list; ///< List for VAR_LIST, can be NULL.
|
list_T *v_list; ///< List for VAR_LIST, can be NULL.
|
||||||
dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
|
dict_T *v_dict; ///< Dictionary for VAR_DICT, can be NULL.
|
||||||
|
partial_T *v_partial; ///< Closure: function with args.
|
||||||
} vval; ///< Actual value.
|
} vval; ///< Actual value.
|
||||||
} typval_T;
|
} typval_T;
|
||||||
|
|
||||||
@@ -144,6 +147,16 @@ struct dictvar_S {
|
|||||||
QUEUE watchers; ///< Dictionary key watchers set by user code.
|
QUEUE watchers; ///< Dictionary key watchers set by user code.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct partial_S {
|
||||||
|
int pt_refcount; ///< Reference count.
|
||||||
|
char_u *pt_name; ///< Function name.
|
||||||
|
bool pt_auto; ///< when true the partial was created for using
|
||||||
|
///< dict.member in handle_subscript().
|
||||||
|
int pt_argc; ///< Number of arguments.
|
||||||
|
typval_T *pt_argv; ///< Arguments in allocated array.
|
||||||
|
dict_T *pt_dict; ///< Dict for "self".
|
||||||
|
};
|
||||||
|
|
||||||
// structure used for explicit stack while garbage collecting hash tables
|
// structure used for explicit stack while garbage collecting hash tables
|
||||||
typedef struct ht_stack_S {
|
typedef struct ht_stack_S {
|
||||||
hashtab_T *ht;
|
hashtab_T *ht;
|
||||||
|
|||||||
@@ -1235,6 +1235,8 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */
|
|||||||
EXTERN int ignored;
|
EXTERN int ignored;
|
||||||
EXTERN char *ignoredp;
|
EXTERN char *ignoredp;
|
||||||
|
|
||||||
|
EXTERN bool in_free_unref_items INIT(= false);
|
||||||
|
|
||||||
// If a msgpack-rpc channel should be started over stdin/stdout
|
// If a msgpack-rpc channel should be started over stdin/stdout
|
||||||
EXTERN bool embedded_mode INIT(= false);
|
EXTERN bool embedded_mode INIT(= false);
|
||||||
|
|
||||||
|
|||||||
@@ -2466,7 +2466,7 @@ do_mouse (
|
|||||||
(int) strlen(tab_page_click_defs[mouse_col].func),
|
(int) strlen(tab_page_click_defs[mouse_col].func),
|
||||||
&rettv, ARRAY_SIZE(argv), argv,
|
&rettv, ARRAY_SIZE(argv), argv,
|
||||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||||
&doesrange, true, NULL);
|
&doesrange, true, NULL, NULL);
|
||||||
clear_tv(&rettv);
|
clear_tv(&rettv);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -790,7 +790,7 @@ do_tag (
|
|||||||
vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
|
vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
|
||||||
set_errorlist(curwin, list, ' ', IObuff);
|
set_errorlist(curwin, list, ' ', IObuff);
|
||||||
|
|
||||||
list_free(list, TRUE);
|
list_free(list);
|
||||||
xfree(fname);
|
xfree(fname);
|
||||||
xfree(cmd);
|
xfree(cmd);
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ source test_matchadd_conceal_utf8.vim
|
|||||||
source test_menu.vim
|
source test_menu.vim
|
||||||
source test_messages.vim
|
source test_messages.vim
|
||||||
source test_options.vim
|
source test_options.vim
|
||||||
|
source test_partial.vim
|
||||||
source test_popup.vim
|
source test_popup.vim
|
||||||
source test_regexp_utf8.vim
|
source test_regexp_utf8.vim
|
||||||
source test_statusline.vim
|
source test_statusline.vim
|
||||||
|
|||||||
@@ -15,6 +15,28 @@ func Test_version()
|
|||||||
call assert_false(has('patch-9.9.1'))
|
call assert_false(has('patch-9.9.1'))
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_equal()
|
||||||
|
let base = {}
|
||||||
|
func base.method()
|
||||||
|
return 1
|
||||||
|
endfunc
|
||||||
|
func base.other() dict
|
||||||
|
return 1
|
||||||
|
endfunc
|
||||||
|
let instance = copy(base)
|
||||||
|
call assert_true(base.method == instance.method)
|
||||||
|
call assert_true([base.method] == [instance.method])
|
||||||
|
call assert_true(base.other == instance.other)
|
||||||
|
call assert_true([base.other] == [instance.other])
|
||||||
|
|
||||||
|
call assert_false(base.method == base.other)
|
||||||
|
call assert_false([base.method] == [base.other])
|
||||||
|
call assert_false(base.method == instance.other)
|
||||||
|
call assert_false([base.method] == [instance.other])
|
||||||
|
|
||||||
|
call assert_fails('echo base.method > instance.method')
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_strgetchar()
|
func Test_strgetchar()
|
||||||
call assert_equal(char2nr('a'), strgetchar('axb', 0))
|
call assert_equal(char2nr('a'), strgetchar('axb', 0))
|
||||||
call assert_equal(char2nr('x'), strgetchar('axb', 1))
|
call assert_equal(char2nr('x'), strgetchar('axb', 1))
|
||||||
|
|||||||
330
src/nvim/testdir/test_partial.vim
Normal file
330
src/nvim/testdir/test_partial.vim
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
" 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 ? 1 : -1
|
||||||
|
endif
|
||||||
|
return a:one < a:two ? 1 : -1
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_partial_args()
|
||||||
|
let Cb = function('MyFunc', ["foo", "bar"])
|
||||||
|
|
||||||
|
call Cb("zzz")
|
||||||
|
call assert_equal("foo/bar/xxx", Cb("xxx"))
|
||||||
|
call assert_equal("foo/bar/yyy", call(Cb, ["yyy"]))
|
||||||
|
let Cb2 = function(Cb)
|
||||||
|
call assert_equal("foo/bar/zzz", Cb2("zzz"))
|
||||||
|
let Cb3 = function(Cb, ["www"])
|
||||||
|
call assert_equal("foo/bar/www", Cb3())
|
||||||
|
|
||||||
|
let Cb = function('MyFunc', [])
|
||||||
|
call assert_equal("a/b/c", Cb("a", "b", "c"))
|
||||||
|
let Cb2 = function(Cb, [])
|
||||||
|
call assert_equal("a/b/d", Cb2("a", "b", "d"))
|
||||||
|
let Cb3 = function(Cb, ["a", "b"])
|
||||||
|
call assert_equal("a/b/e", Cb3("e"))
|
||||||
|
|
||||||
|
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', [], dict)
|
||||||
|
call assert_equal("hello/ttt/xxx", Cb("ttt", "xxx"))
|
||||||
|
call assert_fails('Cb("yyy")', '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("fff")', 'E492:')
|
||||||
|
|
||||||
|
let dict = {"tr": function('tr', ['hello', 'h', 'H'])}
|
||||||
|
call assert_equal("Hello", dict.tr())
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_partial_implicit()
|
||||||
|
let dict = {'name': 'foo'}
|
||||||
|
func dict.MyFunc(arg) dict
|
||||||
|
return self.name . '/' . a:arg
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
call assert_equal('foo/bar', dict.MyFunc('bar'))
|
||||||
|
|
||||||
|
call assert_fails('let func = dict.MyFunc', 'E704:')
|
||||||
|
let Func = dict.MyFunc
|
||||||
|
call assert_equal('foo/aaa', Func('aaa'))
|
||||||
|
|
||||||
|
let Func = function(dict.MyFunc, ['bbb'])
|
||||||
|
call assert_equal('foo/bbb', Func())
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
fun InnerCall(funcref)
|
||||||
|
return a:funcref
|
||||||
|
endfu
|
||||||
|
|
||||||
|
fun OuterCall()
|
||||||
|
let opt = { 'func' : function('sin') }
|
||||||
|
call InnerCall(opt.func)
|
||||||
|
endfu
|
||||||
|
|
||||||
|
func Test_function_in_dict()
|
||||||
|
call OuterCall()
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
function! s:cache_clear() dict
|
||||||
|
return self.name
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
func Test_script_function_in_dict()
|
||||||
|
let s:obj = {'name': 'foo'}
|
||||||
|
let s:obj2 = {'name': 'bar'}
|
||||||
|
|
||||||
|
let s:obj['clear'] = function('s:cache_clear')
|
||||||
|
|
||||||
|
call assert_equal('foo', s:obj.clear())
|
||||||
|
let F = s:obj.clear
|
||||||
|
call assert_equal('foo', F())
|
||||||
|
call assert_equal('foo', call(s:obj.clear, [], s:obj))
|
||||||
|
call assert_equal('bar', call(s:obj.clear, [], s:obj2))
|
||||||
|
|
||||||
|
let s:obj2['clear'] = function('s:cache_clear')
|
||||||
|
call assert_equal('bar', s:obj2.clear())
|
||||||
|
let B = s:obj2.clear
|
||||||
|
call assert_equal('bar', B())
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
function! s:cache_arg(arg) dict
|
||||||
|
let s:result = self.name . '/' . a:arg
|
||||||
|
return s:result
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
func Test_script_function_in_dict_arg()
|
||||||
|
let s:obj = {'name': 'foo'}
|
||||||
|
let s:obj['clear'] = function('s:cache_arg')
|
||||||
|
|
||||||
|
call assert_equal('foo/bar', s:obj.clear('bar'))
|
||||||
|
let F = s:obj.clear
|
||||||
|
let s:result = ''
|
||||||
|
call assert_equal('foo/bar', F('bar'))
|
||||||
|
call assert_equal('foo/bar', s:result)
|
||||||
|
|
||||||
|
let s:obj['clear'] = function('s:cache_arg', ['bar'])
|
||||||
|
call assert_equal('foo/bar', s:obj.clear())
|
||||||
|
let s:result = ''
|
||||||
|
call s:obj.clear()
|
||||||
|
call assert_equal('foo/bar', s:result)
|
||||||
|
|
||||||
|
let F = s:obj.clear
|
||||||
|
call assert_equal('foo/bar', F())
|
||||||
|
let s:result = ''
|
||||||
|
call F()
|
||||||
|
call assert_equal('foo/bar', s:result)
|
||||||
|
|
||||||
|
call assert_equal('foo/bar', call(s:obj.clear, [], s:obj))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_partial_exists()
|
||||||
|
let F = function('MyFunc')
|
||||||
|
call assert_true(exists('*F'))
|
||||||
|
let lF = [F]
|
||||||
|
call assert_true(exists('*lF[0]'))
|
||||||
|
|
||||||
|
let F = function('MyFunc', ['arg'])
|
||||||
|
call assert_true(exists('*F'))
|
||||||
|
let lF = [F]
|
||||||
|
call assert_true(exists('*lF[0]'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_partial_string()
|
||||||
|
let F = function('MyFunc')
|
||||||
|
call assert_equal("function('MyFunc')", string(F))
|
||||||
|
let F = function('MyFunc', ['foo'])
|
||||||
|
call assert_equal("function('MyFunc', ['foo'])", string(F))
|
||||||
|
let F = function('MyFunc', ['foo', 'bar'])
|
||||||
|
call assert_equal("function('MyFunc', ['foo', 'bar'])", string(F))
|
||||||
|
let d = {'one': 1}
|
||||||
|
let F = function('MyFunc', d)
|
||||||
|
call assert_equal("function('MyFunc', {'one': 1})", string(F))
|
||||||
|
let F = function('MyFunc', ['foo'], d)
|
||||||
|
call assert_equal("function('MyFunc', ['foo'], {'one': 1})", string(F))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_func_unref()
|
||||||
|
let obj = {}
|
||||||
|
function! obj.func() abort
|
||||||
|
endfunction
|
||||||
|
let funcnumber = matchstr(string(obj.func), '^function(''\zs.\{-}\ze''')
|
||||||
|
call assert_true(exists('*{' . funcnumber . '}'))
|
||||||
|
unlet obj
|
||||||
|
call assert_false(exists('*{' . funcnumber . '}'))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_redefine_dict_func()
|
||||||
|
let d = {}
|
||||||
|
function d.test4()
|
||||||
|
endfunction
|
||||||
|
let d.test4 = d.test4
|
||||||
|
try
|
||||||
|
function! d.test4(name)
|
||||||
|
endfunction
|
||||||
|
catch
|
||||||
|
call assert_true(v:errmsg, v:exception)
|
||||||
|
endtry
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" This caused double free on exit if EXITFREE is defined.
|
||||||
|
func Test_cyclic_list_arg()
|
||||||
|
let l = []
|
||||||
|
let Pt = function('string', [l])
|
||||||
|
call add(l, Pt)
|
||||||
|
unlet l
|
||||||
|
unlet Pt
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" This caused double free on exit if EXITFREE is defined.
|
||||||
|
func Test_cyclic_dict_arg()
|
||||||
|
let d = {}
|
||||||
|
let Pt = function('string', [d])
|
||||||
|
let d.Pt = Pt
|
||||||
|
unlet d
|
||||||
|
unlet Pt
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Ignored(job1, job2, status)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
" func Test_cycle_partial_job()
|
||||||
|
" let job = job_start('echo')
|
||||||
|
" call job_setoptions(job, {'exit_cb': function('Ignored', [job])})
|
||||||
|
" unlet job
|
||||||
|
" endfunc
|
||||||
|
|
||||||
|
" func Test_ref_job_partial_dict()
|
||||||
|
" let g:ref_job = job_start('echo')
|
||||||
|
" let d = {'a': 'b'}
|
||||||
|
" call job_setoptions(g:ref_job, {'exit_cb': function('string', [], d)})
|
||||||
|
" endfunc
|
||||||
|
|
||||||
|
func Test_auto_partial_rebind()
|
||||||
|
let dict1 = {'name': 'dict1'}
|
||||||
|
func! dict1.f1()
|
||||||
|
return self.name
|
||||||
|
endfunc
|
||||||
|
let dict1.f2 = function(dict1.f1, dict1)
|
||||||
|
|
||||||
|
call assert_equal('dict1', dict1.f1())
|
||||||
|
call assert_equal('dict1', dict1['f1']())
|
||||||
|
call assert_equal('dict1', dict1.f2())
|
||||||
|
call assert_equal('dict1', dict1['f2']())
|
||||||
|
|
||||||
|
let dict2 = {'name': 'dict2'}
|
||||||
|
let dict2.f1 = dict1.f1
|
||||||
|
let dict2.f2 = dict1.f2
|
||||||
|
|
||||||
|
call assert_equal('dict2', dict2.f1())
|
||||||
|
call assert_equal('dict2', dict2['f1']())
|
||||||
|
call assert_equal('dict1', dict2.f2())
|
||||||
|
call assert_equal('dict1', dict2['f2']())
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_get_partial_items()
|
||||||
|
let dict = {'name': 'hello'}
|
||||||
|
let args = ["foo", "bar"]
|
||||||
|
let Func = function('MyDictFunc')
|
||||||
|
let Cb = function('MyDictFunc', args, dict)
|
||||||
|
|
||||||
|
call assert_equal(Func, get(Cb, 'func'))
|
||||||
|
call assert_equal('MyDictFunc', get(Cb, 'name'))
|
||||||
|
call assert_equal(args, get(Cb, 'args'))
|
||||||
|
call assert_equal(dict, get(Cb, 'dict'))
|
||||||
|
call assert_fails('call get(Cb, "xxx")', 'E475:')
|
||||||
|
|
||||||
|
call assert_equal(Func, get(Func, 'func'))
|
||||||
|
call assert_equal('MyDictFunc', get(Func, 'name'))
|
||||||
|
call assert_equal([], get(Func, 'args'))
|
||||||
|
call assert_true(empty( get(Func, 'dict')))
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_compare_partials()
|
||||||
|
let d1 = {}
|
||||||
|
let d2 = {}
|
||||||
|
|
||||||
|
function d1.f1() dict
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
function d1.f2() dict
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
let F1 = get(d1, 'f1')
|
||||||
|
let F2 = get(d1, 'f2')
|
||||||
|
|
||||||
|
let F1d1 = function(F1, d1)
|
||||||
|
let F2d1 = function(F2, d2)
|
||||||
|
let F1d1a1 = function(F1d1, [1])
|
||||||
|
let F1d1a12 = function(F1d1, [1, 2])
|
||||||
|
let F1a1 = function(F1, [1])
|
||||||
|
let F1a2 = function(F1, [2])
|
||||||
|
let F1d2 = function(F1, d2)
|
||||||
|
let d3 = {'f1': F1, 'f2': F2}
|
||||||
|
let F1d3 = function(F1, d3)
|
||||||
|
let F1ad1 = function(F1, [d1])
|
||||||
|
let F1ad3 = function(F1, [d3])
|
||||||
|
|
||||||
|
call assert_match('^function(''\d\+'')$', string(F1)) " Not a partial
|
||||||
|
call assert_match('^function(''\d\+'')$', string(F2)) " Not a partial
|
||||||
|
call assert_match('^function(''\d\+'', {.*})$', string(F1d1)) " A partial
|
||||||
|
call assert_match('^function(''\d\+'', {.*})$', string(F2d1)) " A partial
|
||||||
|
call assert_match('^function(''\d\+'', \[.*\])$', string(F1a1)) " No dict
|
||||||
|
|
||||||
|
" !=
|
||||||
|
let X = F1
|
||||||
|
call assert_false(F1 != X) " same function
|
||||||
|
let X = F1d1
|
||||||
|
call assert_false(F1d1 != X) " same partial
|
||||||
|
let X = F1d1a1
|
||||||
|
call assert_false(F1d1a1 != X) " same partial
|
||||||
|
let X = F1a1
|
||||||
|
call assert_false(F1a1 != X) " same partial
|
||||||
|
|
||||||
|
call assert_true(F1 != F2) " Different functions
|
||||||
|
call assert_true(F1 != F1d1) " Partial /= non-partial
|
||||||
|
call assert_true(F1d1a1 != F1d1a12) " Different number of arguments
|
||||||
|
call assert_true(F1a1 != F1d1a12) " One has no dict
|
||||||
|
call assert_true(F1a1 != F1a2) " Different arguments
|
||||||
|
call assert_true(F1d2 != F1d1) " Different dictionaries
|
||||||
|
call assert_false(F1d1 != F1d3) " Equal dictionaries, even though d1 isnot d3
|
||||||
|
|
||||||
|
" isnot, option 1
|
||||||
|
call assert_true(F1 isnot# F2) " Different functions
|
||||||
|
call assert_true(F1 isnot# F1d1) " Partial /= non-partial
|
||||||
|
call assert_true(F1d1 isnot# F1d3) " d1 isnot d3, even though d1 == d3
|
||||||
|
call assert_true(F1a1 isnot# F1d1a12) " One has no dict
|
||||||
|
call assert_true(F1a1 isnot# F1a2) " Different number of arguments
|
||||||
|
call assert_true(F1ad1 isnot# F1ad3) " In arguments d1 isnot d3
|
||||||
|
|
||||||
|
" isnot, option 2
|
||||||
|
call assert_true(F1 isnot# F2) " Different functions
|
||||||
|
call assert_true(F1 isnot# F1d1) " Partial /= non-partial
|
||||||
|
call assert_true(d1.f1 isnot# d1.f1) " handle_subscript creates new partial each time
|
||||||
|
endfunc
|
||||||
@@ -8,6 +8,10 @@ func MyHandler(timer)
|
|||||||
let s:val += 1
|
let s:val += 1
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func MyHandlerWithLists(lists, timer)
|
||||||
|
let x = string(a:lists)
|
||||||
|
endfunc
|
||||||
|
|
||||||
func Test_oneshot()
|
func Test_oneshot()
|
||||||
let s:val = 0
|
let s:val = 0
|
||||||
let timer = timer_start(50, 'MyHandler')
|
let timer = timer_start(50, 'MyHandler')
|
||||||
@@ -30,3 +34,22 @@ func Test_repeat_many()
|
|||||||
call assert_true(s:val > 1)
|
call assert_true(s:val > 1)
|
||||||
call assert_true(s:val < 5)
|
call assert_true(s:val < 5)
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_with_partial_callback()
|
||||||
|
let s:val = 0
|
||||||
|
let s:meow = {}
|
||||||
|
function s:meow.bite(...)
|
||||||
|
let s:val += 1
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
call timer_start(50, s:meow.bite)
|
||||||
|
sleep 200m
|
||||||
|
call assert_equal(1, s:val)
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_retain_partial()
|
||||||
|
call timer_start(100, function('MyHandlerWithLists', [['a']]))
|
||||||
|
call garbagecollect()
|
||||||
|
sleep 200m
|
||||||
|
endfunc
|
||||||
|
" vim: ts=2 sw=0 et
|
||||||
|
|||||||
@@ -943,6 +943,7 @@ func Test_type()
|
|||||||
call assert_equal(0, type(0))
|
call assert_equal(0, type(0))
|
||||||
call assert_equal(1, type(""))
|
call assert_equal(1, type(""))
|
||||||
call assert_equal(2, type(function("tr")))
|
call assert_equal(2, type(function("tr")))
|
||||||
|
call assert_equal(2, type(function("tr", [8])))
|
||||||
call assert_equal(3, type([]))
|
call assert_equal(3, type([]))
|
||||||
call assert_equal(4, type({}))
|
call assert_equal(4, type({}))
|
||||||
call assert_equal(5, type(0.0))
|
call assert_equal(5, type(0.0))
|
||||||
@@ -983,6 +984,86 @@ func Test_skip()
|
|||||||
|
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
"-------------------------------------------------------------------------------
|
||||||
|
" Test 93: :echo and string() {{{1
|
||||||
|
"-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func Test_echo_and_string()
|
||||||
|
" String
|
||||||
|
let a = 'foo bar'
|
||||||
|
redir => result
|
||||||
|
echo a
|
||||||
|
echo string(a)
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["foo bar",
|
||||||
|
\ "'foo bar'"], l)
|
||||||
|
|
||||||
|
" Float
|
||||||
|
if has('float')
|
||||||
|
let a = -1.2e0
|
||||||
|
redir => result
|
||||||
|
echo a
|
||||||
|
echo string(a)
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["-1.2",
|
||||||
|
\ "-1.2"], l)
|
||||||
|
endif
|
||||||
|
|
||||||
|
" Funcref
|
||||||
|
redir => result
|
||||||
|
echo function('string')
|
||||||
|
echo string(function('string'))
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["string",
|
||||||
|
\ "function('string')"], l)
|
||||||
|
|
||||||
|
" Empty dictionaries in a list
|
||||||
|
let a = {}
|
||||||
|
redir => result
|
||||||
|
echo [a, a, a]
|
||||||
|
echo string([a, a, a])
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["[{}, {}, {}]",
|
||||||
|
\ "[{}, {}, {}]"], l)
|
||||||
|
|
||||||
|
" Empty dictionaries in a dictionary
|
||||||
|
let a = {}
|
||||||
|
let b = {"a": a, "b": a}
|
||||||
|
redir => result
|
||||||
|
echo b
|
||||||
|
echo string(b)
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["{'a': {}, 'b': {}}",
|
||||||
|
\ "{'a': {}, 'b': {}}"], l)
|
||||||
|
|
||||||
|
" Empty lists in a list
|
||||||
|
let a = []
|
||||||
|
redir => result
|
||||||
|
echo [a, a, a]
|
||||||
|
echo string([a, a, a])
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["[[], [], []]",
|
||||||
|
\ "[[], [], []]"], l)
|
||||||
|
|
||||||
|
" Empty lists in a dictionary
|
||||||
|
let a = []
|
||||||
|
let b = {"a": a, "b": a}
|
||||||
|
redir => result
|
||||||
|
echo b
|
||||||
|
echo string(b)
|
||||||
|
redir END
|
||||||
|
let l = split(result, "\n")
|
||||||
|
call assert_equal(["{'a': [], 'b': []}",
|
||||||
|
\ "{'a': [], 'b': []}"], l)
|
||||||
|
|
||||||
|
endfunc
|
||||||
|
|
||||||
"-------------------------------------------------------------------------------
|
"-------------------------------------------------------------------------------
|
||||||
" Modelines {{{1
|
" Modelines {{{1
|
||||||
" vim: ts=8 sw=4 tw=80 fdm=marker
|
" vim: ts=8 sw=4 tw=80 fdm=marker
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ static int included_patches[] = {
|
|||||||
// 1878 NA
|
// 1878 NA
|
||||||
// 1877 NA
|
// 1877 NA
|
||||||
// 1876,
|
// 1876,
|
||||||
// 1875,
|
1875,
|
||||||
// 1874 NA
|
// 1874 NA
|
||||||
// 1873 NA
|
// 1873 NA
|
||||||
// 1872 NA
|
// 1872 NA
|
||||||
@@ -578,9 +578,9 @@ static int included_patches[] = {
|
|||||||
// 1865 NA
|
// 1865 NA
|
||||||
// 1864 NA
|
// 1864 NA
|
||||||
// 1863 NA
|
// 1863 NA
|
||||||
// 1862,
|
// 1862 NA
|
||||||
// 1861,
|
// 1861,
|
||||||
// 1860 NA
|
1860,
|
||||||
// 1859 NA
|
// 1859 NA
|
||||||
// 1858 NA
|
// 1858 NA
|
||||||
// 1857 NA
|
// 1857 NA
|
||||||
@@ -598,15 +598,14 @@ static int included_patches[] = {
|
|||||||
// 1845 NA
|
// 1845 NA
|
||||||
// 1844,
|
// 1844,
|
||||||
// 1843 NA
|
// 1843 NA
|
||||||
// 1842,
|
1842,
|
||||||
// 1841,
|
// 1841,
|
||||||
1840,
|
1840,
|
||||||
// 1839,
|
// 1839,
|
||||||
// 1838,
|
// 1838,
|
||||||
// 1837,
|
// 1837,
|
||||||
// 1836,
|
1836,
|
||||||
1835,
|
1835,
|
||||||
// 1834,
|
|
||||||
1833,
|
1833,
|
||||||
1832,
|
1832,
|
||||||
1831,
|
1831,
|
||||||
@@ -711,7 +710,7 @@ static int included_patches[] = {
|
|||||||
1734,
|
1734,
|
||||||
// 1733 NA
|
// 1733 NA
|
||||||
1732,
|
1732,
|
||||||
// 1731,
|
// 1731 NA
|
||||||
1730,
|
1730,
|
||||||
// 1729 NA
|
// 1729 NA
|
||||||
1728,
|
1728,
|
||||||
@@ -722,12 +721,12 @@ static int included_patches[] = {
|
|||||||
1723,
|
1723,
|
||||||
// 1722 NA
|
// 1722 NA
|
||||||
// 1721 NA
|
// 1721 NA
|
||||||
// 1720,
|
// 1720 NA
|
||||||
// 1719,
|
1719,
|
||||||
// 1718,
|
1718,
|
||||||
// 1717 NA
|
// 1717 NA
|
||||||
1716,
|
1716,
|
||||||
// 1715,
|
1715,
|
||||||
1714,
|
1714,
|
||||||
// 1713 NA
|
// 1713 NA
|
||||||
1712,
|
1712,
|
||||||
@@ -797,14 +796,14 @@ static int included_patches[] = {
|
|||||||
1648,
|
1648,
|
||||||
1647,
|
1647,
|
||||||
// 1646 NA
|
// 1646 NA
|
||||||
// 1645,
|
1645,
|
||||||
// 1644,
|
// 1644 NA
|
||||||
1643,
|
1643,
|
||||||
1642,
|
1642,
|
||||||
1641,
|
1641,
|
||||||
1640,
|
1640,
|
||||||
// 1639,
|
1639,
|
||||||
// 1638,
|
1638,
|
||||||
// 1637 NA
|
// 1637 NA
|
||||||
// 1636 NA
|
// 1636 NA
|
||||||
// 1635 NA
|
// 1635 NA
|
||||||
@@ -834,10 +833,10 @@ static int included_patches[] = {
|
|||||||
// 1611 NA
|
// 1611 NA
|
||||||
// 1610 NA
|
// 1610 NA
|
||||||
// 1609 NA
|
// 1609 NA
|
||||||
// 1608,
|
1608,
|
||||||
// 1607,
|
1607,
|
||||||
// 1606,
|
1606,
|
||||||
// 1605,
|
1605,
|
||||||
1604,
|
1604,
|
||||||
1603,
|
1603,
|
||||||
// 1602 NA
|
// 1602 NA
|
||||||
@@ -852,20 +851,20 @@ static int included_patches[] = {
|
|||||||
// 1593 NA
|
// 1593 NA
|
||||||
1592,
|
1592,
|
||||||
1591,
|
1591,
|
||||||
// 1590,
|
1590,
|
||||||
// 1589,
|
1589,
|
||||||
1588,
|
1588,
|
||||||
// 1587 NA
|
// 1587 NA
|
||||||
// 1586,
|
1586,
|
||||||
// 1585,
|
1585,
|
||||||
// 1584 NA
|
// 1584 NA
|
||||||
// 1583 NA
|
// 1583 NA
|
||||||
// 1582,
|
1582,
|
||||||
// 1581,
|
1581,
|
||||||
// 1580,
|
1580,
|
||||||
// 1579 NA
|
// 1579 NA
|
||||||
1578,
|
1578,
|
||||||
// 1577,
|
1577,
|
||||||
1576,
|
1576,
|
||||||
// 1575 NA
|
// 1575 NA
|
||||||
1574,
|
1574,
|
||||||
@@ -878,12 +877,12 @@ static int included_patches[] = {
|
|||||||
1567,
|
1567,
|
||||||
// 1566 NA
|
// 1566 NA
|
||||||
1565,
|
1565,
|
||||||
// 1564,
|
1564,
|
||||||
// 1563,
|
1563,
|
||||||
// 1562 NA
|
// 1562 NA
|
||||||
// 1561 NA
|
// 1561 NA
|
||||||
// 1560 NA
|
// 1560 NA
|
||||||
// 1559,
|
1559,
|
||||||
1558,
|
1558,
|
||||||
1557,
|
1557,
|
||||||
// 1556 NA
|
// 1556 NA
|
||||||
|
|||||||
27
test/functional/core/job_partial_spec.lua
Normal file
27
test/functional/core/job_partial_spec.lua
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local clear, eq, next_msg, nvim, source = helpers.clear, helpers.eq,
|
||||||
|
helpers.next_message, helpers.nvim, helpers.source
|
||||||
|
|
||||||
|
if helpers.pending_win32(pending) then return end
|
||||||
|
|
||||||
|
describe('jobs with partials', function()
|
||||||
|
local channel
|
||||||
|
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
channel = nvim('get_api_info')[1]
|
||||||
|
nvim('set_var', 'channel', channel)
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works correctly', function()
|
||||||
|
source([[
|
||||||
|
function PrintArgs(a1, a2, id, data, event)
|
||||||
|
call rpcnotify(g:channel, '1', a:a1, a:a2, a:data, a:event)
|
||||||
|
endfunction
|
||||||
|
let Callback = function('PrintArgs', ["foo", "bar"])
|
||||||
|
let g:job_opts = {'on_stdout': Callback}
|
||||||
|
call jobstart(['echo'], g:job_opts)
|
||||||
|
]])
|
||||||
|
eq({'notification', '1', {'foo', 'bar', {'', ''}, 'stdout'}}, next_msg())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
@@ -18,7 +18,7 @@ describe('jobs', function()
|
|||||||
channel = nvim('get_api_info')[1]
|
channel = nvim('get_api_info')[1]
|
||||||
nvim('set_var', 'channel', channel)
|
nvim('set_var', 'channel', channel)
|
||||||
source([[
|
source([[
|
||||||
function! s:OnEvent(id, data, event)
|
function! s:OnEvent(id, data, event) dict
|
||||||
let userdata = get(self, 'user')
|
let userdata = get(self, 'user')
|
||||||
call rpcnotify(g:channel, a:event, userdata, a:data)
|
call rpcnotify(g:channel, a:event, userdata, a:data)
|
||||||
endfunction
|
endfunction
|
||||||
@@ -265,9 +265,12 @@ describe('jobs', function()
|
|||||||
eq({'notification', 'exit', {45, 10}}, next_msg())
|
eq({'notification', 'exit', {45, 10}}, next_msg())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('cannot redefine callbacks being used by a job', function()
|
it('can redefine callbacks being used by a job', function()
|
||||||
local screen = Screen.new()
|
local screen = Screen.new()
|
||||||
screen:attach()
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold=true, foreground=Screen.colors.Blue},
|
||||||
|
})
|
||||||
local script = [[
|
local script = [[
|
||||||
function! g:JobHandler(job_id, data, event)
|
function! g:JobHandler(job_id, data, event)
|
||||||
endfunction
|
endfunction
|
||||||
@@ -283,20 +286,20 @@ describe('jobs', function()
|
|||||||
feed(':function! g:JobHandler(job_id, data, event)<cr>')
|
feed(':function! g:JobHandler(job_id, data, event)<cr>')
|
||||||
feed(':endfunction<cr>')
|
feed(':endfunction<cr>')
|
||||||
screen:expect([[
|
screen:expect([[
|
||||||
~ |
|
^ |
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
~ |
|
{1:~ }|
|
||||||
:function! g:JobHandler(job_id, data, event) |
|
{1:~ }|
|
||||||
: :endfunction |
|
{1:~ }|
|
||||||
E127: Cannot redefine function JobHandler: It is in u|
|
{1:~ }|
|
||||||
se |
|
{1:~ }|
|
||||||
Press ENTER or type command to continue^ |
|
|
|
||||||
]])
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
@@ -317,7 +320,7 @@ describe('jobs', function()
|
|||||||
source([[
|
source([[
|
||||||
let g:dict = {'id': 10}
|
let g:dict = {'id': 10}
|
||||||
let g:exits = 0
|
let g:exits = 0
|
||||||
function g:dict.on_exit(id, code)
|
function g:dict.on_exit(id, code, event)
|
||||||
if a:code != 5
|
if a:code != 5
|
||||||
throw 'Error!'
|
throw 'Error!'
|
||||||
endif
|
endif
|
||||||
@@ -365,7 +368,7 @@ describe('jobs', function()
|
|||||||
eq({'notification', 'wait', {{-2}}}, next_msg())
|
eq({'notification', 'wait', {{-2}}}, next_msg())
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('can be called recursively', function()
|
pending('can be called recursively', function()
|
||||||
source([[
|
source([[
|
||||||
let g:opts = {}
|
let g:opts = {}
|
||||||
let g:counter = 0
|
let g:counter = 0
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ local helpers = require('test.functional.helpers')(after_each)
|
|||||||
local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
|
local clear, nvim, source = helpers.clear, helpers.nvim, helpers.source
|
||||||
local eq, next_msg = helpers.eq, helpers.next_message
|
local eq, next_msg = helpers.eq, helpers.next_message
|
||||||
local exc_exec = helpers.exc_exec
|
local exc_exec = helpers.exc_exec
|
||||||
|
local command = helpers.command
|
||||||
|
|
||||||
|
|
||||||
describe('dictionary change notifications', function()
|
describe('dictionary change notifications', function()
|
||||||
@@ -229,11 +230,9 @@ describe('dictionary change notifications', function()
|
|||||||
exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")'))
|
exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it("fails to add/remove if the callback doesn't exist", function()
|
it("does not fail to add/remove if the callback doesn't exist", function()
|
||||||
eq("Vim(call):Function g:InvalidCb doesn't exist",
|
command('call dictwatcheradd(g:, "key", "g:InvalidCb")')
|
||||||
exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")'))
|
command('call dictwatcherdel(g:, "key", "g:InvalidCb")')
|
||||||
eq("Vim(call):Function g:InvalidCb doesn't exist",
|
|
||||||
exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")'))
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails with empty keys', function()
|
it('fails with empty keys', function()
|
||||||
@@ -243,15 +242,18 @@ describe('dictionary change notifications', function()
|
|||||||
exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")'))
|
exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")'))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('fails to replace a watcher function', function()
|
it('does not fail to replace a watcher function', function()
|
||||||
source([[
|
source([[
|
||||||
function! g:ReplaceWatcher2()
|
function! g:ReplaceWatcher2()
|
||||||
function! g:Watcher2()
|
function! g:Watcher2(dict, key, value)
|
||||||
|
call rpcnotify(g:channel, '2b', a:key, a:value)
|
||||||
endfunction
|
endfunction
|
||||||
endfunction
|
endfunction
|
||||||
]])
|
]])
|
||||||
eq("Vim(function):E127: Cannot redefine function Watcher2: It is in use",
|
command('call g:ReplaceWatcher2()')
|
||||||
exc_exec('call g:ReplaceWatcher2()'))
|
command('let g:key = "value"')
|
||||||
|
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
||||||
|
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|||||||
@@ -274,17 +274,13 @@ describe('list and dictionary types', function()
|
|||||||
let dict.data = [1,2,3]
|
let dict.data = [1,2,3]
|
||||||
call dict.func("len: ")
|
call dict.func("len: ")
|
||||||
let x = dict.func("again: ")
|
let x = dict.func("again: ")
|
||||||
try
|
let Fn = dict.func
|
||||||
let Fn = dict.func
|
call Fn('xxx')]])
|
||||||
call Fn('xxx')
|
|
||||||
catch
|
|
||||||
$put =v:exception[:15]
|
|
||||||
endtry]])
|
|
||||||
expect([[
|
expect([[
|
||||||
|
|
||||||
len: 3
|
len: 3
|
||||||
again: 3
|
again: 3
|
||||||
Vim(call):E725: ]])
|
xxx3]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('Function in script-local List or Dict', function()
|
it('Function in script-local List or Dict', function()
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ describe('terminal buffer', function()
|
|||||||
local tbuf = eval('bufnr("%")')
|
local tbuf = eval('bufnr("%")')
|
||||||
|
|
||||||
source([[
|
source([[
|
||||||
function! SplitWindow()
|
function! SplitWindow(id, data, event)
|
||||||
new
|
new
|
||||||
call feedkeys("iabc\<Esc>")
|
call feedkeys("iabc\<Esc>")
|
||||||
endfunction
|
endfunction
|
||||||
|
|||||||
Reference in New Issue
Block a user