mirror of
https://github.com/neovim/neovim.git
synced 2025-11-25 03:30:37 +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|.
|
||||
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|.
|
||||
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
|
||||
arguments: >
|
||||
: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 ~
|
||||
@@ -1913,10 +1950,12 @@ 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}
|
||||
get({func}, {what}) any get property of funcref/partial {func}
|
||||
getbufline({expr}, {lnum} [, {end}])
|
||||
List lines {lnum} to {end} of buffer {expr}
|
||||
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}
|
||||
|
||||
|
||||
function({name}) *function()* *E700*
|
||||
*function()* *E700* *E922* *E929*
|
||||
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
|
||||
@@ -3510,6 +3585,13 @@ get({dict}, {key} [, {default}])
|
||||
Get item with key {key} from |Dictionary| {dict}. When this
|
||||
item is not available return {default}. Return zero when
|
||||
{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({expr}, {lnum} [, {end}])
|
||||
|
||||
@@ -235,7 +235,7 @@ Additional differences:
|
||||
These legacy Vim features may be implemented in the future, but they are not
|
||||
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_perl|
|
||||
- |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) \
|
||||
TYPVAL_ENCODE_CONV_NIL()
|
||||
|
||||
#define TYPVAL_ENCODE_CONV_PARTIAL(partial) \
|
||||
TYPVAL_ENCODE_CONV_NIL()
|
||||
|
||||
#define TYPVAL_ENCODE_CONV_EMPTY_LIST() \
|
||||
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_FLOAT
|
||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||
#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)) {
|
||||
// cleanup
|
||||
listitem_free(li);
|
||||
list_free(list, true);
|
||||
list_free(list);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -677,7 +681,7 @@ bool object_to_vim(Object obj, typval_T *tv, Error *err)
|
||||
api_set_error(err, Validation,
|
||||
_("Empty dictionary keys aren't allowed"));
|
||||
// cleanup
|
||||
dict_free(dict, true);
|
||||
dict_free(dict);
|
||||
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)) {
|
||||
// cleanup
|
||||
dictitem_free(di);
|
||||
dict_free(dict, true);
|
||||
dict_free(dict);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -225,7 +225,7 @@ Object nvim_call_function(String fname, Array args, Error *err)
|
||||
&rettv, (int) args.size, vim_args,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy,
|
||||
true,
|
||||
NULL);
|
||||
NULL, NULL);
|
||||
if (r == FAIL) {
|
||||
api_set_error(err, Exception, _("Error calling function."));
|
||||
}
|
||||
|
||||
1323
src/nvim/eval.c
1323
src/nvim/eval.c
File diff suppressed because it is too large
Load Diff
@@ -103,7 +103,7 @@ return {
|
||||
foldtext={},
|
||||
foldtextresult={args=1},
|
||||
foreground={},
|
||||
['function']={args=1},
|
||||
['function']={args={1, 3}},
|
||||
garbagecollect={args={0, 1}},
|
||||
get={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, ')'); \
|
||||
} 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() \
|
||||
ga_concat(gap, "[]")
|
||||
|
||||
@@ -661,6 +715,12 @@ static inline int convert_to_json_string(garray_T *const gap,
|
||||
"attempt to dump function reference"), \
|
||||
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()
|
||||
///
|
||||
/// @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_FLOAT
|
||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||
@@ -846,6 +907,11 @@ char *encode_tv2json(typval_T *tv, size_t *len)
|
||||
"attempt to dump function reference"), \
|
||||
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() \
|
||||
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_FLOAT
|
||||
#undef TYPVAL_ENCODE_CONV_FUNC
|
||||
#undef TYPVAL_ENCODE_CONV_PARTIAL
|
||||
#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
|
||||
#undef TYPVAL_ENCODE_CONV_LIST_START
|
||||
#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
|
||||
|
||||
@@ -69,6 +69,11 @@
|
||||
///
|
||||
/// @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
|
||||
/// @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); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_PARTIAL: { \
|
||||
TYPVAL_ENCODE_CONV_PARTIAL(tv->vval.v_partial); \
|
||||
break; \
|
||||
} \
|
||||
case VAR_LIST: { \
|
||||
if (tv->vval.v_list == NULL || tv->vval.v_list->lv_len == 0) { \
|
||||
TYPVAL_ENCODE_CONV_EMPTY_LIST(); \
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef double float_T;
|
||||
|
||||
typedef struct listvar_S list_T;
|
||||
typedef struct dictvar_S dict_T;
|
||||
typedef struct partial_S partial_T;
|
||||
|
||||
/// Special variable values
|
||||
typedef enum {
|
||||
@@ -35,12 +36,13 @@ typedef enum {
|
||||
VAR_UNKNOWN = 0, ///< Unknown (unspecified) value.
|
||||
VAR_NUMBER, ///< Number, .v_number 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_DICT, ///< Dictionary, .v_dict is used.
|
||||
VAR_FLOAT, ///< Floating-point value, .v_float is used.
|
||||
VAR_SPECIAL, ///< Special value (true, false, null), .v_special
|
||||
///< is used.
|
||||
VAR_PARTIAL, ///< Partial, .v_partial is used.
|
||||
} VarType;
|
||||
|
||||
/// 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.
|
||||
list_T *v_list; ///< List for VAR_LIST, 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.
|
||||
} typval_T;
|
||||
|
||||
@@ -144,6 +147,16 @@ struct dictvar_S {
|
||||
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
|
||||
typedef struct ht_stack_S {
|
||||
hashtab_T *ht;
|
||||
|
||||
@@ -1235,6 +1235,8 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */
|
||||
EXTERN int ignored;
|
||||
EXTERN char *ignoredp;
|
||||
|
||||
EXTERN bool in_free_unref_items INIT(= false);
|
||||
|
||||
// If a msgpack-rpc channel should be started over stdin/stdout
|
||||
EXTERN bool embedded_mode INIT(= false);
|
||||
|
||||
|
||||
@@ -2466,7 +2466,7 @@ do_mouse (
|
||||
(int) strlen(tab_page_click_defs[mouse_col].func),
|
||||
&rettv, ARRAY_SIZE(argv), argv,
|
||||
curwin->w_cursor.lnum, curwin->w_cursor.lnum,
|
||||
&doesrange, true, NULL);
|
||||
&doesrange, true, NULL, NULL);
|
||||
clear_tv(&rettv);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -790,7 +790,7 @@ do_tag (
|
||||
vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag);
|
||||
set_errorlist(curwin, list, ' ', IObuff);
|
||||
|
||||
list_free(list, TRUE);
|
||||
list_free(list);
|
||||
xfree(fname);
|
||||
xfree(cmd);
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ source test_matchadd_conceal_utf8.vim
|
||||
source test_menu.vim
|
||||
source test_messages.vim
|
||||
source test_options.vim
|
||||
source test_partial.vim
|
||||
source test_popup.vim
|
||||
source test_regexp_utf8.vim
|
||||
source test_statusline.vim
|
||||
|
||||
@@ -15,6 +15,28 @@ func Test_version()
|
||||
call assert_false(has('patch-9.9.1'))
|
||||
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()
|
||||
call assert_equal(char2nr('a'), strgetchar('axb', 0))
|
||||
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
|
||||
endfunc
|
||||
|
||||
func MyHandlerWithLists(lists, timer)
|
||||
let x = string(a:lists)
|
||||
endfunc
|
||||
|
||||
func Test_oneshot()
|
||||
let s:val = 0
|
||||
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 < 5)
|
||||
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(1, type(""))
|
||||
call assert_equal(2, type(function("tr")))
|
||||
call assert_equal(2, type(function("tr", [8])))
|
||||
call assert_equal(3, type([]))
|
||||
call assert_equal(4, type({}))
|
||||
call assert_equal(5, type(0.0))
|
||||
@@ -983,6 +984,86 @@ func Test_skip()
|
||||
|
||||
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
|
||||
" vim: ts=8 sw=4 tw=80 fdm=marker
|
||||
|
||||
@@ -565,7 +565,7 @@ static int included_patches[] = {
|
||||
// 1878 NA
|
||||
// 1877 NA
|
||||
// 1876,
|
||||
// 1875,
|
||||
1875,
|
||||
// 1874 NA
|
||||
// 1873 NA
|
||||
// 1872 NA
|
||||
@@ -578,9 +578,9 @@ static int included_patches[] = {
|
||||
// 1865 NA
|
||||
// 1864 NA
|
||||
// 1863 NA
|
||||
// 1862,
|
||||
// 1862 NA
|
||||
// 1861,
|
||||
// 1860 NA
|
||||
1860,
|
||||
// 1859 NA
|
||||
// 1858 NA
|
||||
// 1857 NA
|
||||
@@ -598,15 +598,14 @@ static int included_patches[] = {
|
||||
// 1845 NA
|
||||
// 1844,
|
||||
// 1843 NA
|
||||
// 1842,
|
||||
1842,
|
||||
// 1841,
|
||||
1840,
|
||||
// 1839,
|
||||
// 1838,
|
||||
// 1837,
|
||||
// 1836,
|
||||
1836,
|
||||
1835,
|
||||
// 1834,
|
||||
1833,
|
||||
1832,
|
||||
1831,
|
||||
@@ -711,7 +710,7 @@ static int included_patches[] = {
|
||||
1734,
|
||||
// 1733 NA
|
||||
1732,
|
||||
// 1731,
|
||||
// 1731 NA
|
||||
1730,
|
||||
// 1729 NA
|
||||
1728,
|
||||
@@ -722,12 +721,12 @@ static int included_patches[] = {
|
||||
1723,
|
||||
// 1722 NA
|
||||
// 1721 NA
|
||||
// 1720,
|
||||
// 1719,
|
||||
// 1718,
|
||||
// 1720 NA
|
||||
1719,
|
||||
1718,
|
||||
// 1717 NA
|
||||
1716,
|
||||
// 1715,
|
||||
1715,
|
||||
1714,
|
||||
// 1713 NA
|
||||
1712,
|
||||
@@ -797,14 +796,14 @@ static int included_patches[] = {
|
||||
1648,
|
||||
1647,
|
||||
// 1646 NA
|
||||
// 1645,
|
||||
// 1644,
|
||||
1645,
|
||||
// 1644 NA
|
||||
1643,
|
||||
1642,
|
||||
1641,
|
||||
1640,
|
||||
// 1639,
|
||||
// 1638,
|
||||
1639,
|
||||
1638,
|
||||
// 1637 NA
|
||||
// 1636 NA
|
||||
// 1635 NA
|
||||
@@ -834,10 +833,10 @@ static int included_patches[] = {
|
||||
// 1611 NA
|
||||
// 1610 NA
|
||||
// 1609 NA
|
||||
// 1608,
|
||||
// 1607,
|
||||
// 1606,
|
||||
// 1605,
|
||||
1608,
|
||||
1607,
|
||||
1606,
|
||||
1605,
|
||||
1604,
|
||||
1603,
|
||||
// 1602 NA
|
||||
@@ -852,20 +851,20 @@ static int included_patches[] = {
|
||||
// 1593 NA
|
||||
1592,
|
||||
1591,
|
||||
// 1590,
|
||||
// 1589,
|
||||
1590,
|
||||
1589,
|
||||
1588,
|
||||
// 1587 NA
|
||||
// 1586,
|
||||
// 1585,
|
||||
1586,
|
||||
1585,
|
||||
// 1584 NA
|
||||
// 1583 NA
|
||||
// 1582,
|
||||
// 1581,
|
||||
// 1580,
|
||||
1582,
|
||||
1581,
|
||||
1580,
|
||||
// 1579 NA
|
||||
1578,
|
||||
// 1577,
|
||||
1577,
|
||||
1576,
|
||||
// 1575 NA
|
||||
1574,
|
||||
@@ -878,12 +877,12 @@ static int included_patches[] = {
|
||||
1567,
|
||||
// 1566 NA
|
||||
1565,
|
||||
// 1564,
|
||||
// 1563,
|
||||
1564,
|
||||
1563,
|
||||
// 1562 NA
|
||||
// 1561 NA
|
||||
// 1560 NA
|
||||
// 1559,
|
||||
1559,
|
||||
1558,
|
||||
1557,
|
||||
// 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]
|
||||
nvim('set_var', 'channel', channel)
|
||||
source([[
|
||||
function! s:OnEvent(id, data, event)
|
||||
function! s:OnEvent(id, data, event) dict
|
||||
let userdata = get(self, 'user')
|
||||
call rpcnotify(g:channel, a:event, userdata, a:data)
|
||||
endfunction
|
||||
@@ -265,9 +265,12 @@ describe('jobs', function()
|
||||
eq({'notification', 'exit', {45, 10}}, next_msg())
|
||||
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()
|
||||
screen:attach()
|
||||
screen:set_default_attr_ids({
|
||||
[1] = {bold=true, foreground=Screen.colors.Blue},
|
||||
})
|
||||
local script = [[
|
||||
function! g:JobHandler(job_id, data, event)
|
||||
endfunction
|
||||
@@ -283,20 +286,20 @@ describe('jobs', function()
|
||||
feed(':function! g:JobHandler(job_id, data, event)<cr>')
|
||||
feed(':endfunction<cr>')
|
||||
screen:expect([[
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
~ |
|
||||
:function! g:JobHandler(job_id, data, event) |
|
||||
: :endfunction |
|
||||
E127: Cannot redefine function JobHandler: It is in u|
|
||||
se |
|
||||
Press ENTER or type command to continue^ |
|
||||
^ |
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
{1:~ }|
|
||||
|
|
||||
]])
|
||||
end)
|
||||
|
||||
@@ -317,7 +320,7 @@ describe('jobs', function()
|
||||
source([[
|
||||
let g:dict = {'id': 10}
|
||||
let g:exits = 0
|
||||
function g:dict.on_exit(id, code)
|
||||
function g:dict.on_exit(id, code, event)
|
||||
if a:code != 5
|
||||
throw 'Error!'
|
||||
endif
|
||||
@@ -365,7 +368,7 @@ describe('jobs', function()
|
||||
eq({'notification', 'wait', {{-2}}}, next_msg())
|
||||
end)
|
||||
|
||||
it('can be called recursively', function()
|
||||
pending('can be called recursively', function()
|
||||
source([[
|
||||
let g:opts = {}
|
||||
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 eq, next_msg = helpers.eq, helpers.next_message
|
||||
local exc_exec = helpers.exc_exec
|
||||
local command = helpers.command
|
||||
|
||||
|
||||
describe('dictionary change notifications', function()
|
||||
@@ -229,11 +230,9 @@ describe('dictionary change notifications', function()
|
||||
exc_exec('call dictwatcherdel(g:, "invalid_key", "g:Watcher2")'))
|
||||
end)
|
||||
|
||||
it("fails to add/remove if the callback doesn't exist", function()
|
||||
eq("Vim(call):Function g:InvalidCb doesn't exist",
|
||||
exc_exec('call dictwatcheradd(g:, "key", "g:InvalidCb")'))
|
||||
eq("Vim(call):Function g:InvalidCb doesn't exist",
|
||||
exc_exec('call dictwatcherdel(g:, "key", "g:InvalidCb")'))
|
||||
it("does not fail to add/remove if the callback doesn't exist", function()
|
||||
command('call dictwatcheradd(g:, "key", "g:InvalidCb")')
|
||||
command('call dictwatcherdel(g:, "key", "g:InvalidCb")')
|
||||
end)
|
||||
|
||||
it('fails with empty keys', function()
|
||||
@@ -243,15 +242,18 @@ describe('dictionary change notifications', function()
|
||||
exc_exec('call dictwatcherdel(g:, "", "g:Watcher1")'))
|
||||
end)
|
||||
|
||||
it('fails to replace a watcher function', function()
|
||||
it('does not fail to replace a watcher function', function()
|
||||
source([[
|
||||
function! g:ReplaceWatcher2()
|
||||
function! g:Watcher2()
|
||||
function! g:Watcher2(dict, key, value)
|
||||
call rpcnotify(g:channel, '2b', a:key, a:value)
|
||||
endfunction
|
||||
endfunction
|
||||
]])
|
||||
eq("Vim(function):E127: Cannot redefine function Watcher2: It is in use",
|
||||
exc_exec('call g:ReplaceWatcher2()'))
|
||||
command('call g:ReplaceWatcher2()')
|
||||
command('let g:key = "value"')
|
||||
eq({'notification', '2b', {'key', {old = 'v2', new = 'value'}}}, next_msg())
|
||||
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
|
||||
@@ -274,17 +274,13 @@ describe('list and dictionary types', function()
|
||||
let dict.data = [1,2,3]
|
||||
call dict.func("len: ")
|
||||
let x = dict.func("again: ")
|
||||
try
|
||||
let Fn = dict.func
|
||||
call Fn('xxx')
|
||||
catch
|
||||
$put =v:exception[:15]
|
||||
endtry]])
|
||||
call Fn('xxx')]])
|
||||
expect([[
|
||||
|
||||
len: 3
|
||||
again: 3
|
||||
Vim(call):E725: ]])
|
||||
xxx3]])
|
||||
end)
|
||||
|
||||
it('Function in script-local List or Dict', function()
|
||||
|
||||
@@ -167,7 +167,7 @@ describe('terminal buffer', function()
|
||||
local tbuf = eval('bufnr("%")')
|
||||
|
||||
source([[
|
||||
function! SplitWindow()
|
||||
function! SplitWindow(id, data, event)
|
||||
new
|
||||
call feedkeys("iabc\<Esc>")
|
||||
endfunction
|
||||
|
||||
Reference in New Issue
Block a user