mirror of
https://github.com/neovim/neovim.git
synced 2025-09-06 11:28:22 +00:00
fix(api): correctly pass f-args for nvim_create_user_command (#18098)
Skip runs of whitespace and do not include `\` characters when followed by another `\` or whitespace. This matches the behavior of <f-args> when used with `:command`.
This commit is contained in:
@@ -5774,26 +5774,44 @@ static void ex_delcommand(exarg_T *eap)
|
|||||||
/// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback.
|
/// Split a string by unescaped whitespace (space & tab), used for f-args on Lua commands callback.
|
||||||
/// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator.
|
/// Similar to uc_split_args(), but does not allocate, add quotes, add commas and is an iterator.
|
||||||
///
|
///
|
||||||
/// @note If no separator is found start = 0 and end = length - 1
|
|
||||||
/// @param[in] arg String to split
|
/// @param[in] arg String to split
|
||||||
/// @param[in] iter Iteration counter
|
/// @param[in] arglen Length of {arg}
|
||||||
/// @param[out] start Start of the split
|
/// @param[inout] end Index of last character of previous iteration
|
||||||
/// @param[out] end End of the split
|
/// @param[out] buf Buffer to copy string into
|
||||||
/// @param[in] length Length of the string
|
/// @param[out] len Length of string in {buf}
|
||||||
///
|
///
|
||||||
/// @return false if it's the last split (don't call again), true otherwise (call again).
|
/// @return true if iteration is complete, else false
|
||||||
bool uc_split_args_iter(const char_u *arg, int iter, int *start, int *end, int length)
|
bool uc_split_args_iter(const char_u *arg, size_t arglen, size_t *end, char *buf, size_t *len)
|
||||||
{
|
{
|
||||||
int pos;
|
if (!arglen) {
|
||||||
*start = *end + (iter > 1 ? 2 : 0); // Skip whitespace after the first split
|
|
||||||
for (pos = *start; pos < length - 2; pos++) {
|
|
||||||
if (arg[pos] != '\\' && ascii_iswhite(arg[pos + 1])) {
|
|
||||||
*end = pos;
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t pos = *end;
|
||||||
|
while (pos < arglen && ascii_iswhite(arg[pos])) {
|
||||||
|
pos++;
|
||||||
}
|
}
|
||||||
*end = length - 1;
|
|
||||||
|
size_t l = 0;
|
||||||
|
for (; pos < arglen - 1; pos++) {
|
||||||
|
if (arg[pos] == '\\' && (arg[pos + 1] == '\\' || ascii_iswhite(arg[pos + 1]))) {
|
||||||
|
buf[l++] = arg[++pos];
|
||||||
|
} else {
|
||||||
|
buf[l++] = arg[pos];
|
||||||
|
if (ascii_iswhite(arg[pos + 1])) {
|
||||||
|
*end = pos + 1;
|
||||||
|
*len = l;
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pos < arglen && !ascii_iswhite(arg[pos])) {
|
||||||
|
buf[l++] = arg[pos];
|
||||||
|
}
|
||||||
|
|
||||||
|
*len = l;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// split and quote args for <f-args>
|
/// split and quote args for <f-args>
|
||||||
|
@@ -1869,18 +1869,22 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
|
|||||||
} else {
|
} else {
|
||||||
// Commands with more than one possible argument we split
|
// Commands with more than one possible argument we split
|
||||||
lua_pop(lstate, 1); // Pop the reference of opts.args
|
lua_pop(lstate, 1); // Pop the reference of opts.args
|
||||||
int length = (int)STRLEN(eap->arg);
|
size_t length = STRLEN(eap->arg);
|
||||||
int start = 0;
|
size_t end = 0;
|
||||||
int end = 0;
|
size_t len = 0;
|
||||||
int i = 1;
|
int i = 1;
|
||||||
bool res = true;
|
char *buf = xcalloc(length, sizeof(char));
|
||||||
while (res) {
|
bool done = false;
|
||||||
res = uc_split_args_iter(eap->arg, i, &start, &end, length);
|
while (!done) {
|
||||||
lua_pushlstring(lstate, (const char *)eap->arg + start, (size_t)(end - start + 1));
|
done = uc_split_args_iter(eap->arg, length, &end, buf, &len);
|
||||||
|
if (len > 0) {
|
||||||
|
lua_pushlstring(lstate, buf, len);
|
||||||
lua_rawseti(lstate, -2, i);
|
lua_rawseti(lstate, -2, i);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
xfree(buf);
|
||||||
|
}
|
||||||
lua_setfield(lstate, -2, "fargs");
|
lua_setfield(lstate, -2, "fargs");
|
||||||
|
|
||||||
lua_pushstring(lstate, (const char *)&eap->regname);
|
lua_pushstring(lstate, (const char *)&eap->regname);
|
||||||
|
@@ -114,8 +114,8 @@ describe('nvim_create_user_command', function()
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
eq({
|
eq({
|
||||||
args = [[hello my\ friend how\ are\ you?]],
|
args = [[this is a\ test]],
|
||||||
fargs = {[[hello]], [[my\ friend]], [[how\ are\ you?]]},
|
fargs = {"this", "is", "a test"},
|
||||||
bang = false,
|
bang = false,
|
||||||
line1 = 1,
|
line1 = 1,
|
||||||
line2 = 1,
|
line2 = 1,
|
||||||
@@ -124,12 +124,42 @@ describe('nvim_create_user_command', function()
|
|||||||
count = 2,
|
count = 2,
|
||||||
reg = "",
|
reg = "",
|
||||||
}, exec_lua [=[
|
}, exec_lua [=[
|
||||||
vim.api.nvim_command([[CommandWithLuaCallback hello my\ friend how\ are\ you?]])
|
vim.api.nvim_command([[CommandWithLuaCallback this is a\ test]])
|
||||||
return result
|
return result
|
||||||
]=])
|
]=])
|
||||||
|
|
||||||
eq({
|
eq({
|
||||||
args = 'h\tey',
|
args = [[this includes\ a backslash: \\]],
|
||||||
|
fargs = {"this", "includes a", "backslash:", "\\"},
|
||||||
|
bang = false,
|
||||||
|
line1 = 1,
|
||||||
|
line2 = 1,
|
||||||
|
mods = "",
|
||||||
|
range = 0,
|
||||||
|
count = 2,
|
||||||
|
reg = "",
|
||||||
|
}, exec_lua [=[
|
||||||
|
vim.api.nvim_command([[CommandWithLuaCallback this includes\ a backslash: \\]])
|
||||||
|
return result
|
||||||
|
]=])
|
||||||
|
|
||||||
|
eq({
|
||||||
|
args = "a\\b",
|
||||||
|
fargs = {"a\\b"},
|
||||||
|
bang = false,
|
||||||
|
line1 = 1,
|
||||||
|
line2 = 1,
|
||||||
|
mods = "",
|
||||||
|
range = 0,
|
||||||
|
count = 2,
|
||||||
|
reg = "",
|
||||||
|
}, exec_lua [=[
|
||||||
|
vim.api.nvim_command('CommandWithLuaCallback a\\b')
|
||||||
|
return result
|
||||||
|
]=])
|
||||||
|
|
||||||
|
eq({
|
||||||
|
args = 'h\tey ',
|
||||||
fargs = {[[h]], [[ey]]},
|
fargs = {[[h]], [[ey]]},
|
||||||
bang = true,
|
bang = true,
|
||||||
line1 = 10,
|
line1 = 10,
|
||||||
@@ -139,7 +169,7 @@ describe('nvim_create_user_command', function()
|
|||||||
count = 10,
|
count = 10,
|
||||||
reg = "",
|
reg = "",
|
||||||
}, exec_lua [=[
|
}, exec_lua [=[
|
||||||
vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey')
|
vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey ')
|
||||||
return result
|
return result
|
||||||
]=])
|
]=])
|
||||||
|
|
||||||
@@ -160,7 +190,7 @@ describe('nvim_create_user_command', function()
|
|||||||
|
|
||||||
eq({
|
eq({
|
||||||
args = "",
|
args = "",
|
||||||
fargs = {""}, -- fargs works without args
|
fargs = {}, -- fargs works without args
|
||||||
bang = false,
|
bang = false,
|
||||||
line1 = 1,
|
line1 = 1,
|
||||||
line2 = 1,
|
line2 = 1,
|
||||||
@@ -186,8 +216,8 @@ describe('nvim_create_user_command', function()
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
eq({
|
eq({
|
||||||
args = "hello I'm one argmuent",
|
args = "hello I'm one argument",
|
||||||
fargs = {"hello I'm one argmuent"}, -- Doesn't split args
|
fargs = {"hello I'm one argument"}, -- Doesn't split args
|
||||||
bang = false,
|
bang = false,
|
||||||
line1 = 1,
|
line1 = 1,
|
||||||
line2 = 1,
|
line2 = 1,
|
||||||
@@ -196,7 +226,7 @@ describe('nvim_create_user_command', function()
|
|||||||
count = 2,
|
count = 2,
|
||||||
reg = "",
|
reg = "",
|
||||||
}, exec_lua [[
|
}, exec_lua [[
|
||||||
vim.api.nvim_command('CommandWithOneArg hello I\'m one argmuent')
|
vim.api.nvim_command('CommandWithOneArg hello I\'m one argument')
|
||||||
return result
|
return result
|
||||||
]])
|
]])
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user