Problem: Duplicate code for translating script-local function name.
Solution: Move the code to get_scriptlocal_funcname(). (Yegappan Lakshmanan,
closes #9393)
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.
- sprintf(sid_buf, "<SNR>%ld_", (long)current_sctx.sc_sid);
- name = alloc(STRLEN(sid_buf) + STRLEN(s + off) + 1);
- if (name != NULL)
- {
- STRCPY(name, sid_buf);
- STRCAT(name, s + off);
- }
- }
+ name = get_scriptlocal_funcname(s);
else
name = vim_strsave(s);
r = FAIL;
else if (arg->v_type == VAR_FUNC || arg->v_type == VAR_STRING)
{
- // Note that we don't make a copy of the string.
+ if (arg->v_type == VAR_STRING)
+ {
+ char_u *name;
+
+ name = get_scriptlocal_funcname(arg->vval.v_string);
+ if (name != NULL)
+ {
+ vim_free(arg->vval.v_string);
+ arg->vval.v_string = name;
+ }
+ }
+
res.cb_name = arg->vval.v_string;
func_ref(res.cb_name);
}
// Lambda expression or a funcref
tv = eval_expr(optval, NULL);
else
- {
// treat everything else as a function name string
-
- // Function name starting with "s:" are supported only in a vimscript
- // context.
- if (STRNCMP(optval, "s:", 2) == 0)
- {
- char sid_buf[25];
- char_u *funcname;
-
- if (!SCRIPT_ID_VALID(current_sctx.sc_sid))
- {
- emsg(_(e_using_sid_not_in_script_context));
- return FAIL;
- }
- // Expand s: prefix into <SNR>nr_<name>
- sprintf(sid_buf, "<SNR>%ld_", (long)current_sctx.sc_sid);
- funcname = alloc(STRLEN(sid_buf) + STRLEN(optval + 2) + 1);
- if (funcname == NULL)
- return FAIL;
- STRCPY(funcname, sid_buf);
- STRCAT(funcname, optval + 2);
- tv = alloc_string_tv(funcname);
- }
- else
- tv = alloc_string_tv(vim_strsave(optval));
- }
+ tv = alloc_string_tv(vim_strsave(optval));
if (tv == NULL)
return FAIL;
char_u *printable_func_name(ufunc_T *fp);
char_u *trans_function_name(char_u **pp, int *is_global, int skip, int flags, funcdict_T *fdp, partial_T **partial, type_T **type);
char_u *untrans_function_name(char_u *name);
+char_u *get_scriptlocal_funcname(char_u *funcname);
char_u *save_function_name(char_u **name, int *is_global, int skip, int flags, funcdict_T *fudi);
void list_functions(regmatch_T *regmatch);
ufunc_T *define_function(exarg_T *eap, char_u *name_arg);
call assert_fails('echo test_null_function()->funcref()', 'E475: Invalid argument: NULL')
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()
let lines =<< trim END
hi def link 1 Comment
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
return NULL;
}
+/*
+ * 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_u *
+get_scriptlocal_funcname(char_u *funcname)
+{
+ char sid_buf[25];
+ int off;
+ char_u *newname;
+
+ 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_using_sid_not_in_script_context));
+ return NULL;
+ }
+ // Expand s: prefix into <SNR>nr_<name>
+ vim_snprintf(sid_buf, sizeof(sid_buf), "<SNR>%ld_",
+ (long)current_sctx.sc_sid);
+ off = *funcname == 's' ? 2 : 5;
+ newname = alloc(STRLEN(sid_buf) + STRLEN(funcname + off) + 1);
+ if (newname == NULL)
+ return NULL;
+ 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.
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 3889,
/**/
3888,
/**/