diff options
-rw-r--r-- | src/nvim/ex_docmd.c | 52 | ||||
-rw-r--r-- | src/nvim/lua/executor.c | 22 | ||||
-rw-r--r-- | test/functional/api/command_spec.lua | 48 |
3 files changed, 87 insertions, 35 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 482bf69f67..cbfe6e3789 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -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. /// 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] iter Iteration counter -/// @param[out] start Start of the split -/// @param[out] end End of the split -/// @param[in] length Length of the string +/// @param[in] arg String to split +/// @param[in] arglen Length of {arg} +/// @param[inout] end Index of last character of previous iteration +/// @param[out] buf Buffer to copy string into +/// @param[out] len Length of string in {buf} /// -/// @return false if it's the last split (don't call again), true otherwise (call again). -bool uc_split_args_iter(const char_u *arg, int iter, int *start, int *end, int length) -{ - int pos; - *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 if iteration is complete, else false +bool uc_split_args_iter(const char_u *arg, size_t arglen, size_t *end, char *buf, size_t *len) +{ + if (!arglen) { + return true; + } + + size_t pos = *end; + while (pos < arglen && ascii_iswhite(arg[pos])) { + pos++; + } + + 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; + } } } - *end = length - 1; - return false; + + if (pos < arglen && !ascii_iswhite(arg[pos])) { + buf[l++] = arg[pos]; + } + + *len = l; + return true; } /// split and quote args for <f-args> diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 21625854fb..81396f1715 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -1869,17 +1869,21 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) } else { // Commands with more than one possible argument we split lua_pop(lstate, 1); // Pop the reference of opts.args - int length = (int)STRLEN(eap->arg); - int start = 0; - int end = 0; + size_t length = STRLEN(eap->arg); + size_t end = 0; + size_t len = 0; int i = 1; - bool res = true; - while (res) { - res = uc_split_args_iter(eap->arg, i, &start, &end, length); - lua_pushlstring(lstate, (const char *)eap->arg + start, (size_t)(end - start + 1)); - lua_rawseti(lstate, -2, i); - i++; + char *buf = xcalloc(length, sizeof(char)); + bool done = false; + while (!done) { + done = uc_split_args_iter(eap->arg, length, &end, buf, &len); + if (len > 0) { + lua_pushlstring(lstate, buf, len); + lua_rawseti(lstate, -2, i); + i++; + } } + xfree(buf); } lua_setfield(lstate, -2, "fargs"); diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index ad162473ec..e4963e8a65 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -114,8 +114,8 @@ describe('nvim_create_user_command', function() ]] eq({ - args = [[hello my\ friend how\ are\ you?]], - fargs = {[[hello]], [[my\ friend]], [[how\ are\ you?]]}, + args = [[this is a\ test]], + fargs = {"this", "is", "a test"}, bang = false, line1 = 1, line2 = 1, @@ -124,12 +124,42 @@ describe('nvim_create_user_command', function() count = 2, reg = "", }, exec_lua [=[ - vim.api.nvim_command([[CommandWithLuaCallback hello my\ friend how\ are\ you?]]) + vim.api.nvim_command([[CommandWithLuaCallback this is a\ test]]) return result ]=]) 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]]}, bang = true, line1 = 10, @@ -139,7 +169,7 @@ describe('nvim_create_user_command', function() count = 10, reg = "", }, exec_lua [=[ - vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey') + vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey ') return result ]=]) @@ -160,7 +190,7 @@ describe('nvim_create_user_command', function() eq({ args = "", - fargs = {""}, -- fargs works without args + fargs = {}, -- fargs works without args bang = false, line1 = 1, line2 = 1, @@ -186,8 +216,8 @@ describe('nvim_create_user_command', function() ]] eq({ - args = "hello I'm one argmuent", - fargs = {"hello I'm one argmuent"}, -- Doesn't split args + args = "hello I'm one argument", + fargs = {"hello I'm one argument"}, -- Doesn't split args bang = false, line1 = 1, line2 = 1, @@ -196,7 +226,7 @@ describe('nvim_create_user_command', function() count = 2, reg = "", }, exec_lua [[ - vim.api.nvim_command('CommandWithOneArg hello I\'m one argmuent') + vim.api.nvim_command('CommandWithOneArg hello I\'m one argument') return result ]]) |