diff options
-rw-r--r-- | runtime/doc/eval.txt | 16 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 11 | ||||
-rw-r--r-- | src/nvim/ex_cmds.lua | 6 | ||||
-rw-r--r-- | src/nvim/ex_docmd.c | 1 | ||||
-rw-r--r-- | src/nvim/ex_eval.c | 9 | ||||
-rw-r--r-- | src/nvim/syntax.c | 18 | ||||
-rw-r--r-- | src/nvim/testdir/test_expr.vim | 9 | ||||
-rw-r--r-- | src/nvim/testdir/test_functions.vim | 16 | ||||
-rw-r--r-- | src/nvim/testdir/test_syntax.vim | 45 |
9 files changed, 119 insertions, 12 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index c3736d9a3e..cc84bb6c97 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -10099,6 +10099,8 @@ 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. + AUTOMATICALLY LOADING FUNCTIONS ~ *autoload-functions* @@ -10532,6 +10534,20 @@ text... Unlock the internal variable {name}. Does the opposite of |:lockvar|. + *:eval* +:eval {expr} Evaluate {expr} and discard the result. Example: > + :eval append(Filter(Getlist()), '$') + +< The expression is supposed to have a side effect, + since the resulting value is not used. In the example + the `append()` call appends the List with text to the + buffer. This is similar to `:call` but works with any + expression. + + The command can be shortened to `:ev` or `:eva`, but + these are hard to recognize and therefore not to be + used. + :if {expr1} *:if* *:end* *:endif* *:en* *E171* *E579* *E580* :en[dif] Execute the commands until the next matching ":else" diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 689d05e079..00260bc3f7 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -833,6 +833,8 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, bool islambda = false; char_u numbuf[NUMBUFLEN]; char_u *name; + typval_T *tv_to_free[MAX_FUNC_ARGS]; + int tv_to_free_len = 0; proftime_T wait_start; proftime_T call_start; int started_profiling = false; @@ -985,6 +987,11 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, v->di_tv = isdefault ? def_rettv : argvars[i]; v->di_tv.v_lock = VAR_FIXED; + if (isdefault) { + // Need to free this later, no matter where it's stored. + tv_to_free[tv_to_free_len++] = &v->di_tv; + } + if (addlocal) { // Named arguments can be accessed without the "a:" prefix in lambda // expressions. Add to the l: dict. @@ -1209,7 +1216,9 @@ void call_user_func(ufunc_T *fp, int argcount, typval_T *argvars, did_emsg |= save_did_emsg; depth--; - + for (int i = 0; i < tv_to_free_len; i++) { + tv_clear(tv_to_free[i]); + } cleanup_function_call(fc); if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) { diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index 2965ea7496..d99383303b 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -928,6 +928,12 @@ module.cmds = { func='ex_edit', }, { + command='eval', + flags=bit.bor(EXTRA, NOTRLCOM, SBOXOK, CMDWIN), + addr_type='ADDR_NONE', + func='ex_eval', + }, + { command='ex', flags=bit.bor(BANG, FILE1, CMDARG, ARGOPT, TRLBAR), addr_type='ADDR_NONE', diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index d1eddfc74f..ae5c334592 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1857,6 +1857,7 @@ static char_u * do_one_cmd(char_u **cmdlinep, case CMD_echoerr: case CMD_echomsg: case CMD_echon: + case CMD_eval: case CMD_execute: case CMD_filter: case CMD_help: diff --git a/src/nvim/ex_eval.c b/src/nvim/ex_eval.c index 0917c6dd02..5ca88002f1 100644 --- a/src/nvim/ex_eval.c +++ b/src/nvim/ex_eval.c @@ -788,6 +788,15 @@ void report_discard_pending(int pending, void *value) } } +// ":eval". +void ex_eval(exarg_T *eap) +{ + typval_T tv; + + if (eval0(eap->arg, &tv, &eap->nextcmd, !eap->skip) == OK) { + tv_clear(&tv); + } +} /* * ":if". diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 825aef1465..77a751e5ad 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -5306,13 +5306,17 @@ get_id_list( xfree(name); break; } - if (name[1] == 'A') - id = SYNID_ALLBUT; - else if (name[1] == 'T') - id = SYNID_TOP; - else - id = SYNID_CONTAINED; - id += current_syn_inc_tag; + if (name[1] == 'A') { + id = SYNID_ALLBUT + current_syn_inc_tag; + } else if (name[1] == 'T') { + if (curwin->w_s->b_syn_topgrp >= SYNID_CLUSTER) { + id = curwin->w_s->b_syn_topgrp; + } else { + id = SYNID_TOP + current_syn_inc_tag; + } + } else { + id = SYNID_CONTAINED + current_syn_inc_tag; + } } else if (name[1] == '@') { if (skip) { id = -1; diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index 09d79979ce..0b41a1127a 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -501,3 +501,12 @@ func Test_empty_concatenate() call assert_equal('b', 'a'[4:0] . 'b') call assert_equal('b', 'b' . 'a'[4:0]) endfunc + +func Test_eval_after_if() + let s:val = '' + func SetVal(x) + let s:val ..= a:x + endfunc + if 0 | eval SetVal('a') | endif | call SetVal('b') + call assert_equal('b', s:val) +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index 555f549743..93f567b3a0 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1071,10 +1071,10 @@ func Test_inputlist() endfunc func Test_balloon_show() - if has('balloon_eval') - " This won't do anything but must not crash either. - call balloon_show('hi!') - endif + CheckFeature balloon_eval + + " This won't do anything but must not crash either. + call balloon_show('hi!') endfunc func Test_shellescape() @@ -1448,4 +1448,12 @@ func Test_nr2char() call assert_equal("\x80\xfc\b\xfd\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX\x80\xfeX", eval('"\<M-' .. nr2char(0x40000000) .. '>"')) endfunc +func HasDefault(msg = 'msg') + return a:msg +endfunc + +func Test_default_arg_value() + call assert_equal('msg', HasDefault()) +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index 66cb0bbe22..875e23894f 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -24,6 +24,32 @@ func GetSyntaxItem(pat) return c endfunc +func AssertHighlightGroups(lnum, startcol, expected, trans = 1, msg = "") + " Assert that the characters starting at a given (line, col) + " sequentially match the expected highlight groups. + " If groups are provided as a string, each character is assumed to be a + " group and spaces represent no group, useful for visually describing tests. + let l:expectedGroups = type(a:expected) == v:t_string + "\ ? a:expected->split('\zs')->map({_, v -> trim(v)}) + \ ? map(split(a:expected, '\zs'), {_, v -> trim(v)}) + \ : a:expected + let l:errors = 0 + " let l:msg = (a:msg->empty() ? "" : a:msg .. ": ") + let l:msg = (empty(a:msg) ? "" : a:msg .. ": ") + \ .. "Wrong highlight group at " .. a:lnum .. "," + + " for l:i in range(a:startcol, a:startcol + l:expectedGroups->len() - 1) + " let l:errors += synID(a:lnum, l:i, a:trans) + " \ ->synIDattr("name") + " \ ->assert_equal(l:expectedGroups[l:i - 1], + for l:i in range(a:startcol, a:startcol + len(l:expectedGroups) - 1) + let l:errors += + \ assert_equal(synIDattr(synID(a:lnum, l:i, a:trans), "name"), + \ l:expectedGroups[l:i - 1], + \ l:msg .. l:i) + endfor +endfunc + func Test_syn_iskeyword() new call setline(1, [ @@ -707,3 +733,22 @@ func Test_syntax_foldlevel() quit! endfunc + +func Test_syn_include_contains_TOP() + let l:case = "TOP in included syntax means its group list name" + new + syntax include @INCLUDED syntax/c.vim + syntax region FencedCodeBlockC start=/```c/ end=/```/ contains=@INCLUDED + + call setline(1, ['```c', '#if 0', 'int', '#else', 'int', '#endif', '```' ]) + let l:expected = ["cCppOutIf2"] + eval AssertHighlightGroups(3, 1, l:expected, 1) + " cCppOutElse has contains=TOP + let l:expected = ["cType"] + eval AssertHighlightGroups(5, 1, l:expected, 1, l:case) + syntax clear + bw! +endfunc + + +" vim: shiftwidth=2 sts=2 expandtab |