mirror of
https://github.com/neovim/neovim.git
synced 2026-07-12 12:29:44 +00:00
feat(eval): treat Lua string as "blob" in writefile() #39098
Problem: vim.fn.writefile() treats Lua strings as Vimscript strings instead of a "binary clean" string. Solution: Treat Lua-originated strings as blob data.
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/path.h"
|
#include "nvim/path.h"
|
||||||
#include "nvim/pos_defs.h"
|
#include "nvim/pos_defs.h"
|
||||||
|
#include "nvim/runtime.h"
|
||||||
#include "nvim/strings.h"
|
#include "nvim/strings.h"
|
||||||
#include "nvim/types_defs.h"
|
#include "nvim/types_defs.h"
|
||||||
#include "nvim/vim_defs.h"
|
#include "nvim/vim_defs.h"
|
||||||
@@ -1716,13 +1717,12 @@ write_list_error:
|
|||||||
/// @param[in] blob Blob to write.
|
/// @param[in] blob Blob to write.
|
||||||
///
|
///
|
||||||
/// @return true on success, or false on failure.
|
/// @return true on success, or false on failure.
|
||||||
static bool write_blob(FileDescriptor *const fp, const blob_T *const blob)
|
static bool write_data(FileDescriptor *const fp, const char *const data, const size_t len)
|
||||||
FUNC_ATTR_NONNULL_ARG(1)
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
{
|
{
|
||||||
int error = 0;
|
int error = 0;
|
||||||
const int len = tv_blob_len(blob);
|
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
const ptrdiff_t written = file_write(fp, blob->bv_ga.ga_data, (size_t)len);
|
const ptrdiff_t written = file_write(fp, data, len);
|
||||||
if (written < (ptrdiff_t)len) {
|
if (written < (ptrdiff_t)len) {
|
||||||
error = (int)written;
|
error = (int)written;
|
||||||
goto write_blob_error;
|
goto write_blob_error;
|
||||||
@@ -1738,6 +1738,18 @@ write_blob_error:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool write_blob(FileDescriptor *const fp, const blob_T *const blob)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
|
{
|
||||||
|
return write_data(fp, blob->bv_ga.ga_data, (size_t)tv_blob_len(blob));
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write_string(FileDescriptor *const fp, const char *const data)
|
||||||
|
FUNC_ATTR_NONNULL_ARG(1)
|
||||||
|
{
|
||||||
|
return write_data(fp, data, strlen(data));
|
||||||
|
}
|
||||||
|
|
||||||
/// "writefile()" function
|
/// "writefile()" function
|
||||||
void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
||||||
{
|
{
|
||||||
@@ -1753,7 +1765,8 @@ void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else if (argvars[0].v_type != VAR_BLOB) {
|
} else if (argvars[0].v_type != VAR_BLOB
|
||||||
|
&& !(argvars[0].v_type == VAR_STRING && script_is_lua(current_sctx.sc_sid))) {
|
||||||
semsg(_(e_invarg2),
|
semsg(_(e_invarg2),
|
||||||
_("writefile() first argument must be a List or a Blob"));
|
_("writefile() first argument must be a List or a Blob"));
|
||||||
return;
|
return;
|
||||||
@@ -1823,6 +1836,8 @@ void f_writefile(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
|
|||||||
bool write_ok;
|
bool write_ok;
|
||||||
if (argvars[0].v_type == VAR_BLOB) {
|
if (argvars[0].v_type == VAR_BLOB) {
|
||||||
write_ok = write_blob(&fp, argvars[0].vval.v_blob);
|
write_ok = write_blob(&fp, argvars[0].vval.v_blob);
|
||||||
|
} else if (argvars[0].v_type == VAR_STRING) {
|
||||||
|
write_ok = write_string(&fp, argvars[0].vval.v_string);
|
||||||
} else {
|
} else {
|
||||||
write_ok = write_list(&fp, argvars[0].vval.v_list, binary);
|
write_ok = write_list(&fp, argvars[0].vval.v_list, binary);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1366,12 +1366,14 @@ int nlua_call(lua_State *lstate)
|
|||||||
funcexe.fe_firstline = curwin->w_cursor.lnum;
|
funcexe.fe_firstline = curwin->w_cursor.lnum;
|
||||||
funcexe.fe_lastline = curwin->w_cursor.lnum;
|
funcexe.fe_lastline = curwin->w_cursor.lnum;
|
||||||
funcexe.fe_evaluate = true;
|
funcexe.fe_evaluate = true;
|
||||||
|
const sctx_T save_current_sctx = api_set_sctx(LUA_INTERNAL_CALL);
|
||||||
|
|
||||||
TRY_WRAP(&err, {
|
TRY_WRAP(&err, {
|
||||||
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
|
// call_func() retval is deceptive, ignore it. Instead we set `msg_list`
|
||||||
// (TRY_WRAP) to capture abort-causing non-exception errors.
|
// (TRY_WRAP) to capture abort-causing non-exception errors.
|
||||||
(void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
|
(void)call_func(name, (int)name_len, &rettv, nargs, vim_args, &funcexe);
|
||||||
});
|
});
|
||||||
|
current_sctx = save_current_sctx;
|
||||||
|
|
||||||
if (!ERROR_SET(&err)) {
|
if (!ERROR_SET(&err)) {
|
||||||
nlua_push_typval(lstate, &rettv, 0);
|
nlua_push_typval(lstate, &rettv, 0);
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ local clear = n.clear
|
|||||||
local eq = t.eq
|
local eq = t.eq
|
||||||
local fn = n.fn
|
local fn = n.fn
|
||||||
local api = n.api
|
local api = n.api
|
||||||
|
local exec_lua = n.exec_lua
|
||||||
local read_file = t.read_file
|
local read_file = t.read_file
|
||||||
local write_file = t.write_file
|
local write_file = t.write_file
|
||||||
local pcall_err = t.pcall_err
|
local pcall_err = t.pcall_err
|
||||||
@@ -99,6 +100,11 @@ describe('writefile()', function()
|
|||||||
eq('a\0', read_file(fname))
|
eq('a\0', read_file(fname))
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
it('writes Lua strings to a file', function()
|
||||||
|
eq(0, exec_lua([[return vim.fn.writefile('foo\0bar', ..., 'b')]], fname))
|
||||||
|
eq('foo\0bar', read_file(fname))
|
||||||
|
end)
|
||||||
|
|
||||||
it('shows correct file name when supplied numbers', function()
|
it('shows correct file name when supplied numbers', function()
|
||||||
api.nvim_set_current_dir(dname)
|
api.nvim_set_current_dir(dname)
|
||||||
eq(
|
eq(
|
||||||
|
|||||||
Reference in New Issue
Block a user