aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2021-08-06 17:09:47 +0100
committerSean Dewar <seandewar@users.noreply.github.com>2021-08-12 22:35:20 +0100
commit003c8acc8a9863932430bfb51bee8403b964c19b (patch)
treec7f47d87cbc6152a25dbaeffea225842ffc8306b
parente6be6c307a832d661d2a6269ad2d322e4bf5e9cc (diff)
downloadrneovim-003c8acc8a9863932430bfb51bee8403b964c19b.tar.gz
rneovim-003c8acc8a9863932430bfb51bee8403b964c19b.tar.bz2
rneovim-003c8acc8a9863932430bfb51bee8403b964c19b.zip
vim-patch:8.1.1807: more functions can be used as a method
Problem: More functions can be used as a method. Solution: Add append(), appendbufline(), assert_equal(), etc. Also add the :eval command. https://github.com/vim/vim/commit/25e42231d3ee27feec2568fa4be2aa2bfba82ae5 :eval is already ported.
-rw-r--r--runtime/doc/eval.txt43
-rw-r--r--runtime/doc/testing.txt10
-rw-r--r--src/nvim/eval.lua16
-rw-r--r--src/nvim/eval/funcs.c5
-rw-r--r--src/nvim/eval/funcs.h6
-rw-r--r--src/nvim/generators/gen_eval.lua4
-rw-r--r--src/nvim/testdir/test_method.vim25
7 files changed, 83 insertions, 26 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index eee401baa1..070c94da3b 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -669,12 +669,14 @@ Expression syntax summary, from least to most significant:
expr8[expr1 : expr1] substring of a String or sublist of a |List|
expr8.name entry in a |Dictionary|
expr8(expr1, ...) function call with |Funcref| variable
+ expr8->name(expr1, ...) |method| call
|expr9| number number constant
"string" string constant, backslash is special
'string' string constant, ' is doubled
[expr1, ...] |List|
{expr1: expr1, ...} |Dictionary|
+ #{key: expr1, ...} |Dictionary|
&option option value
(expr1) nested expression
variable internal variable
@@ -939,10 +941,10 @@ expr8 *expr8*
-----
This expression is either |expr9| or a sequence of the alternatives below,
in any order. E.g., these are all possible:
- expr9[expr1].name
- expr9.name[expr1]
- expr9(expr1, ...)[expr1].name
- expr9->(expr1, ...)[expr1]
+ expr8[expr1].name
+ expr8.name[expr1]
+ expr8(expr1, ...)[expr1].name
+ expr8->(expr1, ...)[expr1]
Evaluation is always from left to right.
@@ -1047,10 +1049,17 @@ When expr8 is a |Funcref| type variable, invoke the function it refers to.
expr8->name([args]) method call *method*
-For global methods this is the same as: >
+For methods that are also available as global functions this is the same as: >
name(expr8 [, args])
There can also be methods specifically for the type of "expr8".
+"->name(" must not contain white space. There can be white space before "->"
+and after the "(".
+
+This allows for chaining, using the type that the method returns: >
+ mylist->filter(filterexpr)->map(mapexpr)->sort()->join()
+<
+
*expr9*
number
------
@@ -2637,6 +2646,9 @@ append({lnum}, {text}) *append()*
:let failed = append(line('$'), "# THE END")
:let failed = append(0, ["Chapter 1", "the beginning"])
+< Can also be used as a |method| after a List: >
+ mylist->append(lnum)
+
appendbufline({expr}, {lnum}, {text}) *appendbufline()*
Like |append()| but append the text in buffer {expr}.
@@ -2655,8 +2667,10 @@ appendbufline({expr}, {lnum}, {text}) *appendbufline()*
error message is given. Example: >
:let failed = appendbufline(13, 0, "# THE START")
<
- *argc()*
-argc([{winid}])
+ Can also be used as a |method| after a List: >
+ mylist->appendbufline(buf, lnum)
+
+argc([{winid}]) *argc()*
The result is the number of files in the argument list. See
|arglist|.
If {winid} is not supplied, the argument list of the current
@@ -3518,6 +3532,9 @@ eval({string}) Evaluate {string} and return the result. Especially useful to
them. Also works for |Funcref|s that refer to existing
functions.
+ Can also be used as a |method|: >
+ argv->join()->eval()
+
eventhandler() *eventhandler()*
Returns 1 when inside an event handler. That is that Vim got
interrupted while waiting for the user to type a character,
@@ -3864,7 +3881,11 @@ filereadable({file}) *filereadable()*
expression, which is used as a String.
If you don't care about the file being readable you can use
|glob()|.
-
+ {file} is used as-is, you may want to expand wildcards first: >
+ echo filereadable('~/.vimrc')
+ 0
+ echo filereadable(expand('~/.vimrc'))
+ 1
filewritable({file}) *filewritable()*
The result is a Number, which is 1 when a file with the
@@ -10006,7 +10027,9 @@ This function can then be called with: >
The recursiveness of user functions is restricted with the |'maxfuncdepth'|
option.
-It is also possible to use `:eval`. It does not support a range.
+It is also possible to use `:eval`. It does not support a range, but does
+allow for method chaining, e.g.: >
+ eval GetList()->Filter()->append('$')
AUTOMATICALLY LOADING FUNCTIONS ~
@@ -10749,7 +10772,7 @@ text...
<
*:eval*
:eval {expr} Evaluate {expr} and discard the result. Example: >
- :eval append(Filter(Getlist()), '$')
+ :eval Getlist()->Filter()->append('$')
< The expression is supposed to have a side effect,
since the resulting value is not used. In the example
diff --git a/runtime/doc/testing.txt b/runtime/doc/testing.txt
index ef8d6b5ea9..eca2025ed7 100644
--- a/runtime/doc/testing.txt
+++ b/runtime/doc/testing.txt
@@ -69,7 +69,10 @@ assert_equal({expected}, {actual} [, {msg}])
< Will result in a string to be added to |v:errors|:
test.vim line 12: Expected 'foo' but got 'bar' ~
- *assert_equalfile()*
+ Can also be used as a |method|: >
+ mylist->assert_equal([1, 2, 3])
+
+< *assert_equalfile()*
assert_equalfile({fname-one}, {fname-two})
When the files {fname-one} and {fname-two} do not contain
exactly the same text an error message is added to |v:errors|.
@@ -145,7 +148,10 @@ assert_notequal({expected}, {actual} [, {msg}])
|v:errors| when {expected} and {actual} are equal.
Also see |assert-return|.
- *assert_notmatch()*
+ Can also be used as a |method|: >
+ mylist->assert_notequal([1, 2, 3])
+
+< *assert_notmatch()*
assert_notmatch({pattern}, {actual} [, {msg}])
The opposite of `assert_match()`: add an error message to
|v:errors| when {pattern} matches {actual}.
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index f94ecffed9..506368a3b2 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -7,7 +7,7 @@
-- arguments.
-- base For methods: the argument to use as the base argument (1-indexed):
-- base->method()
--- Defaults to zero (function cannot be used as a method).
+-- Defaults to BASE_NONE (function cannot be used as a method).
-- func Name of the C function which implements the VimL function. Defaults to
-- `f_{funcname}`.
@@ -15,6 +15,10 @@ local varargs = function(nr)
return {nr}
end
+-- Usable with the base key: use the last function argument as the method base.
+-- Value is from funcs.h file. "BASE_" prefix is omitted.
+local LAST = "BASE_LAST"
+
return {
funcs={
abs={args=1},
@@ -22,15 +26,15 @@ return {
add={args=2, base=1},
['and']={args=2},
api_info={},
- append={args=2},
- appendbufline={args=3},
+ append={args=2, base=LAST},
+ appendbufline={args=3, base=LAST},
argc={args={0, 1}},
argidx={},
arglistid={args={0, 2}},
argv={args={0, 2}},
asin={args=1, func="float_op_wrapper", data="&asin"}, -- WJMc
assert_beeps={args={1}},
- assert_equal={args={2, 3}},
+ assert_equal={args={2, 3}, base=2},
assert_equalfile={args={2, 3}},
assert_exception={args={1, 2}},
assert_fails={args={1, 3}},
@@ -38,7 +42,7 @@ return {
assert_inrange={args={3, 4}},
assert_match={args={2, 3}},
assert_nobeep={args={1}},
- assert_notequal={args={2, 3}},
+ assert_notequal={args={2, 3}, base=2},
assert_notmatch={args={2, 3}},
assert_report={args=1},
assert_true={args={1, 2}},
@@ -99,7 +103,7 @@ return {
empty={args=1, base=1},
environ={},
escape={args=2},
- eval={args=1},
+ eval={args=1, base=1},
eventhandler={},
executable={args=1},
execute={args={1, 2}},
diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c
index 787d5aaf78..e090f3b37f 100644
--- a/src/nvim/eval/funcs.c
+++ b/src/nvim/eval/funcs.c
@@ -199,7 +199,7 @@ int call_internal_method(const char_u *const fname, const int argcount,
FUNC_ATTR_NONNULL_ALL
{
const VimLFuncDef *const fdef = find_internal_func((const char *)fname);
- if (fdef == NULL || fdef->base_arg == 0) {
+ if (fdef == NULL || fdef->base_arg == BASE_NONE) {
return ERROR_UNKNOWN;
} else if (argcount + 1 < fdef->min_argc) {
return ERROR_TOOFEW;
@@ -208,7 +208,8 @@ int call_internal_method(const char_u *const fname, const int argcount,
}
typval_T argv[MAX_FUNC_ARGS + 1];
- const ptrdiff_t base_index = fdef->base_arg - 1;
+ const ptrdiff_t base_index
+ = fdef->base_arg == BASE_LAST ? argcount : fdef->base_arg - 1;
memcpy(argv, argvars, base_index * sizeof(typval_T));
argv[base_index] = *basetv;
memcpy(argv + base_index + 1, argvars + base_index,
diff --git a/src/nvim/eval/funcs.h b/src/nvim/eval/funcs.h
index 3ad0b8282a..c6a0cb959e 100644
--- a/src/nvim/eval/funcs.h
+++ b/src/nvim/eval/funcs.h
@@ -9,12 +9,16 @@ typedef void (*FunPtr)(void);
/// Prototype of C function that implements VimL function
typedef void (*VimLFunc)(typval_T *args, typval_T *rvar, FunPtr data);
+/// Special flags for base_arg @see VimLFuncDef
+#define BASE_NONE 0 ///< Not a method (no base argument).
+#define BASE_LAST UINT8_MAX ///< Use the last argument as the method base.
+
/// Structure holding VimL function definition
typedef struct fst {
char *name; ///< Name of the function.
uint8_t min_argc; ///< Minimal number of arguments.
uint8_t max_argc; ///< Maximal number of arguments.
- uint8_t base_arg; ///< Method base arg # (1-indexed), or 0 if not a method.
+ uint8_t base_arg; ///< Method base arg # (1-indexed), BASE_NONE or BASE_LAST.
VimLFunc func; ///< Function implementation.
FunPtr data; ///< Userdata for function implementation.
} VimLFuncDef;
diff --git a/src/nvim/generators/gen_eval.lua b/src/nvim/generators/gen_eval.lua
index aa2274e0a2..945fa5099f 100644
--- a/src/nvim/generators/gen_eval.lua
+++ b/src/nvim/generators/gen_eval.lua
@@ -42,7 +42,7 @@ gperfpipe:write([[
%language=ANSI-C
%global-table
%readonly-tables
-%define initializer-suffix ,0,0,0,NULL,NULL
+%define initializer-suffix ,0,0,BASE_NONE,NULL,NULL
%define word-array-name functions
%define hash-function-name hash_internal_func_gperf
%define lookup-function-name find_internal_func_gperf
@@ -59,7 +59,7 @@ for name, def in pairs(funcs) do
elseif #args == 1 then
args[2] = 'MAX_FUNC_ARGS'
end
- local base = def.base or 0
+ local base = def.base or "BASE_NONE"
local func = def.func or ('f_' .. name)
local data = def.data or "NULL"
gperfpipe:write(('%s, %s, %s, %s, &%s, (FunPtr)%s\n')
diff --git a/src/nvim/testdir/test_method.vim b/src/nvim/testdir/test_method.vim
index 1c0de01ac5..43ed830aba 100644
--- a/src/nvim/testdir/test_method.vim
+++ b/src/nvim/testdir/test_method.vim
@@ -3,18 +3,23 @@
func Test_list()
let l = [1, 2, 3]
call assert_equal([1, 2, 3, 4], [1, 2, 3]->add(4))
+ eval l->assert_equal(l)
+ eval l->assert_equal(l, 'wrong')
+ eval l->assert_notequal([3, 2, 1])
+ eval l->assert_notequal([3, 2, 1], 'wrong')
call assert_equal(l, l->copy())
call assert_equal(1, l->count(2))
call assert_false(l->empty())
call assert_true([]->empty())
+ call assert_equal(579, ['123', '+', '456']->join()->eval())
call assert_equal([1, 2, 3, 4, 5], [1, 2, 3]->extend([4, 5]))
call assert_equal([1, 3], [1, 2, 3]->filter('v:val != 2'))
call assert_equal(2, l->get(1))
call assert_equal(1, l->index(2))
call assert_equal([0, 1, 2, 3], [1, 2, 3]->insert(0))
- call assert_fails('let x = l->items()', 'E715:')
+ call assert_fails('eval l->items()', 'E715:')
call assert_equal('1 2 3', l->join())
- call assert_fails('let x = l->keys()', 'E715:')
+ call assert_fails('eval l->keys()', 'E715:')
call assert_equal(3, l->len())
call assert_equal([2, 3, 4], [1, 2, 3]->map('v:val + 1'))
call assert_equal(3, l->max())
@@ -26,7 +31,7 @@ func Test_list()
call assert_equal('[1, 2, 3]', l->string())
call assert_equal(v:t_list, l->type())
call assert_equal([1, 2, 3], [1, 1, 2, 3, 3]->uniq())
- call assert_fails('let x = l->values()', 'E715:')
+ call assert_fails('eval l->values()', 'E715:')
endfunc
func Test_dict()
@@ -65,4 +70,18 @@ func Test_dict()
call assert_equal([1, 2, 3], d->values())
endfunc
+func Test_append()
+ new
+ eval ['one', 'two', 'three']->append(1)
+ call assert_equal(['', 'one', 'two', 'three'], getline(1, '$'))
+
+ %del
+ let bnr = bufnr('')
+ wincmd w
+ eval ['one', 'two', 'three']->appendbufline(bnr, 1)
+ call assert_equal(['', 'one', 'two', 'three'], getbufline(bnr, 1, '$'))
+
+ exe 'bwipe! ' .. bnr
+endfunc
+
" vim: shiftwidth=2 sts=2 expandtab