commit f15c28ed7a852ccab8de6ac3e38d167187cc183e
parent eac2f0443e032ba238ca6b4a9e2fd6135be454b3
Author: zeertzjq <zeertzjq@outlook.com>
Date: Fri, 19 Dec 2025 15:27:09 +0800
vim-patch:8.2.3766: converting a funcref to a string leaves out "g:"
Problem: Converting a funcref to a string leaves out "g:", causing the
meaning of the name depending on the context.
Solution: Prepend "g:" for a global function.
https://github.com/vim/vim/commit/c4ec338fb80ebfb5d631f0718fdd1a1c04d9ed82
Co-authored-by: Bram Moolenaar <Bram@vim.org>
Co-authored-by: Jan Edmund Lazo <jan.lazo@mail.utoronto.ca>
Diffstat:
6 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/src/nvim/api/private/converter.c b/src/nvim/api/private/converter.c
@@ -74,7 +74,7 @@ static Object typval_cbuf_to_obj(EncodedData *edata, const char *data, size_t le
kvi_push(edata->stack, typval_cbuf_to_obj(edata, len_ ? blob_->bv_ga.ga_data : "", len_)); \
} while (0)
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
do { \
ufunc_T *fp = find_func(fun); \
if (fp != NULL && (fp->uf_flags & FC_LUAREF)) { \
diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c
@@ -295,7 +295,7 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
do { \
const char *const buf_ = (buf); \
- if ((buf) == NULL) { \
+ if (buf_ == NULL) { \
ga_concat(gap, "''"); \
} else { \
const size_t len_ = (len); \
@@ -370,15 +370,21 @@ int encode_read_from_list(ListReaderState *const state, char *const buf, const s
} \
} while (0)
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
do { \
const char *const fun_ = (fun); \
if (fun_ == NULL) { \
internal_error("string(): NULL function name"); \
ga_concat(gap, "function(NULL"); \
} else { \
+ const char *const prefix_ = (prefix); \
ga_concat(gap, "function("); \
+ const int name_off = gap->ga_len; \
+ ga_concat(gap, prefix_); \
TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
+ /* <prefix>'<fun>' -> '<prefix><fun>'. */ \
+ ((char *)gap->ga_data)[name_off] = '\''; \
+ memcpy((char *)gap->ga_data + name_off + 1, prefix_, strlen(prefix_)); \
} \
} while (0)
@@ -748,7 +754,7 @@ static inline int convert_to_json_string(garray_T *const gap, const char *const
} while (0)
#undef TYPVAL_ENCODE_CONV_FUNC_START
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
return conv_error(_("E474: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
mpstack, objname)
@@ -932,7 +938,7 @@ char *encode_tv2json(typval_T *tv, size_t *len)
#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
mpack_float8(&packer->ptr, (double)(flt))
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
return conv_error(_("E5004: Error while dumping %s, %s: " \
"attempt to dump function reference"), \
mpstack, objname)
diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c
@@ -3436,7 +3436,7 @@ static inline int _nothing_conv_func_start(typval_T *const tv, char *const fun)
}
return NOTDONE;
}
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
do { \
if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \
return OK; \
diff --git a/src/nvim/eval/typval_encode.c.h b/src/nvim/eval/typval_encode.c.h
@@ -102,6 +102,7 @@
///
/// @param tv Pointer to typval where value is stored. May not be NULL.
/// @param fun Function name. May be NULL.
+/// @param prefix Prefix for converting to a string.
/// @def TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
/// @brief Macros used before starting to convert partial arguments
@@ -344,15 +345,19 @@ static int TYPVAL_ENCODE_CONVERT_ONE_VALUE(
tv_blob_len(tv->vval.v_blob));
break;
case VAR_FUNC:
- TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string);
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, tv->vval.v_string, "");
TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, 0);
TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, -1);
TYPVAL_ENCODE_CONV_FUNC_END(tv);
break;
case VAR_PARTIAL: {
partial_T *const pt = tv->vval.v_partial;
- (void)pt;
- TYPVAL_ENCODE_CONV_FUNC_START(tv, (pt == NULL ? NULL : partial_name(pt)));
+ char *const fun = pt == NULL ? NULL : partial_name(pt);
+ // When using uf_name prepend "g:" for a global function.
+ const char *const prefix = fun != NULL && pt->pt_name == NULL
+ && ASCII_ISUPPER(fun[0]) ? "g:" : "";
+ (void)prefix;
+ TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix);
kvi_push(*mpstack, ((MPConvStackVal) {
.type = kMPConvPartial,
.tv = tv,
diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c
@@ -453,7 +453,7 @@ static bool typval_conv_special = false;
lua_pushlstring(lstate, blob_ != NULL ? blob_->bv_ga.ga_data : "", (size_t)(len)); \
} while (0)
-#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
+#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun, prefix) \
do { \
ufunc_T *fp = find_func(fun); \
if (fp != NULL && fp->uf_flags & FC_LUAREF) { \
diff --git a/test/old/testdir/test_functions.vim b/test/old/testdir/test_functions.vim
@@ -3719,6 +3719,11 @@ func Test_builtin_check()
unlet bar
endfunc
+func Test_funcref_to_string()
+ let Fn = funcref('g:Test_funcref_to_string')
+ call assert_equal("function('g:Test_funcref_to_string')", string(Fn))
+endfunc
+
" Test for isabsolutepath()
func Test_isabsolutepath()
call assert_false(isabsolutepath(''))