diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/nvim/eval.c | 20 | ||||
-rw-r--r-- | src/nvim/eval/userfunc.c | 34 | ||||
-rw-r--r-- | src/nvim/option.c | 21 | ||||
-rw-r--r-- | src/nvim/quickfix.c | 6 | ||||
-rw-r--r-- | src/nvim/testdir/test_expr.vim | 15 | ||||
-rw-r--r-- | src/nvim/testdir/test_normal.vim | 12 | ||||
-rw-r--r-- | src/nvim/testdir/test_quickfix.vim | 28 |
7 files changed, 106 insertions, 30 deletions
diff --git a/src/nvim/eval.c b/src/nvim/eval.c index c4afd6934c..6d8f4d092c 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5018,18 +5018,11 @@ void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) int arg_idx = 0; list_T *list = NULL; if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { - char sid_buf[25]; - int off = *s == 's' ? 2 : 5; - // Expand s: and <SID> into <SNR>nr_, so that the function can // also be called from another script. Using trans_function_name() // would also work, but some plugins depend on the name being // printable text. - snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", - (int64_t)current_sctx.sc_sid); - name = xmalloc(strlen(sid_buf) + strlen(s + off) + 1); - STRCPY(name, sid_buf); - STRCAT(name, s + off); + name = get_scriptlocal_funcname(s); } else { name = xstrdup(s); } @@ -5517,6 +5510,7 @@ void get_system_output_as_rettv(typval_T *argvars, typval_T *rettv, bool retlist } } +/// Get a callback from "arg". It can be a Funcref or a function name. bool callback_from_typval(Callback *const callback, typval_T *const arg) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { @@ -5538,8 +5532,14 @@ bool callback_from_typval(Callback *const callback, typval_T *const arg) callback->type = kCallbackNone; callback->data.funcref = NULL; } else { - func_ref((char_u *)name); - callback->data.funcref = xstrdup(name); + callback->data.funcref = NULL; + if (arg->v_type == VAR_STRING) { + callback->data.funcref = get_scriptlocal_funcname(name); + } + if (callback->data.funcref == NULL) { + callback->data.funcref = xstrdup(name); + } + func_ref((char_u *)callback->data.funcref); callback->type = kCallbackFuncref; } } else if (nlua_is_table_from_lua(arg)) { diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 359ce08554..a37613dca9 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -1930,6 +1930,40 @@ theend: return (char_u *)name; } +/// If the "funcname" starts with "s:" or "<SID>", then expands it to the +/// current script ID and returns the expanded function name. The caller should +/// free the returned name. If not called from a script context or the function +/// name doesn't start with these prefixes, then returns NULL. +/// This doesn't check whether the script-local function exists or not. +char *get_scriptlocal_funcname(char *funcname) +{ + if (funcname == NULL) { + return NULL; + } + + if (strncmp(funcname, "s:", 2) != 0 + && strncmp(funcname, "<SID>", 5) != 0) { + // The function name is not a script-local function name + return NULL; + } + + if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) { + emsg(_(e_usingsid)); + return NULL; + } + + char sid_buf[25]; + // Expand s: and <SID> prefix into <SNR>nr_<name> + snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", + (int64_t)current_sctx.sc_sid); + const int off = *funcname == 's' ? 2 : 5; + char *newname = xmalloc(strlen(sid_buf) + strlen(funcname + off) + 1); + STRCPY(newname, sid_buf); + STRCAT(newname, funcname + off); + + return newname; +} + /// Call trans_function_name(), except that a lambda is returned as-is. /// Returns the name in allocated memory. char *save_function_name(char **name, bool skip, int flags, funcdict_T *fudi) diff --git a/src/nvim/option.c b/src/nvim/option.c index 6d461d9b9d..e67bacce61 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5170,26 +5170,7 @@ int option_set_callback_func(char *optval, Callback *optcb) // treat everything else as a function name string tv = xcalloc(1, sizeof(*tv)); tv->v_type = VAR_STRING; - - // Function name starting with "s:" are supported only in a vimscript - // context. - if (strncmp(optval, "s:", 2) == 0) { - char sid_buf[25]; - - if (!SCRIPT_ID_VALID(current_sctx.sc_sid)) { - emsg(_(e_usingsid)); - return FAIL; - } - // Expand s: prefix into <SNR>nr_<name> - snprintf(sid_buf, sizeof(sid_buf), "<SNR>%" PRId64 "_", - (int64_t)current_sctx.sc_sid); - char *funcname = xmalloc(strlen(sid_buf) + strlen(optval + 2) + 1); - STRCPY(funcname, sid_buf); - STRCAT(funcname, optval + 2); - tv->vval.v_string = funcname; - } else { - tv->vval.v_string = xstrdup(optval); - } + tv->vval.v_string = xstrdup(optval); } Callback cb; diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index deac0bb8a1..d4dff746c7 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -3900,6 +3900,9 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) qf_winid = (int)win->handle; } + // autocommands may cause trouble + incr_quickfix_busy(); + aco_save_T aco; if (old_last == NULL) { @@ -3924,6 +3927,9 @@ static void qf_update_buffer(qf_info_T *qi, qfline_T *old_last) if ((win = qf_find_win(qi)) != NULL && old_line_count < win->w_botline) { redraw_buf_later(buf, UPD_NOT_VALID); } + + // always called after incr_quickfix_busy() + decr_quickfix_busy(); } } diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index f33358c59a..47f7f5eb0e 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -525,6 +525,21 @@ func Test_funcref() call assert_fails('echo function("min") =~ function("min")', 'E694:') endfunc +" Test for calling function() and funcref() outside of a Vim script context. +func Test_function_outside_script() + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim') + call RunVim([], [], "-c \"echo function('s:abc')\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call RunVim([], [], "-c \"echo funcref('s:abc')\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + call delete('Xverify.vim') +endfunc + func Test_setmatches() hi def link 1 Comment hi def link 2 PreProc diff --git a/src/nvim/testdir/test_normal.vim b/src/nvim/testdir/test_normal.vim index d9b392992f..9c5cc51f79 100644 --- a/src/nvim/testdir/test_normal.vim +++ b/src/nvim/testdir/test_normal.vim @@ -658,6 +658,18 @@ func Test_opfunc_callback() END " call CheckScriptSuccess(lines) + " setting 'opfunc' to a script local function outside of a script context + " should fail + let cleanup =<< trim END + call writefile([execute('messages')], 'Xtest.out') + qall + END + call writefile(cleanup, 'Xverify.vim') + call RunVim([], [], "-c \"set opfunc=s:abc\" -S Xverify.vim") + call assert_match('E81: Using <SID> not in a', readfile('Xtest.out')[0]) + call delete('Xtest.out') + call delete('Xverify.vim') + " cleanup set opfunc& delfunc OpFunc1 diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 02cee8a8dd..7b94c4027c 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -3298,6 +3298,21 @@ func Test_resize_from_copen() endtry endfunc +func Test_filetype_autocmd() + " this changes the location list while it is in use to fill a buffer + lexpr '' + lopen + augroup FT_loclist + au FileType * call setloclist(0, [], 'f') + augroup END + silent! lolder + lexpr '' + + augroup FT_loclist + au! FileType + augroup END +endfunc + func Test_vimgrep_with_textlock() new @@ -6165,4 +6180,17 @@ func Test_loclist_replace_autocmd() call setloclist(0, [], 'f') endfunc +func s:QfTf(_) +endfunc + +func Test_setqflist_cb_arg() + " This was changing the callback name in the dictionary. + let d = #{quickfixtextfunc: 's:QfTf'} + call setqflist([], 'a', d) + call assert_equal('s:QfTf', d.quickfixtextfunc) + + call setqflist([], 'f') +endfunc + + " vim: shiftwidth=2 sts=2 expandtab |