mirror of
				https://github.com/neovim/neovim.git
				synced 2025-10-26 12:27:24 +00:00 
			
		
		
		
	feat(lua): add <f-args> to user commands callback (#17522)
Works similar to ex <f-args>. It only splits the arguments if the
command has more than one posible argument. In cases were the command
can only have 1 argument opts.fargs = { opts.args }
			
			
This commit is contained in:
		| @@ -651,6 +651,9 @@ nvim_add_user_command({name}, {command}, {*opts}) | |||||||
|                                that contains the following keys: |                                that contains the following keys: | ||||||
|                                • args: (string) The args passed to the |                                • args: (string) The args passed to the | ||||||
|                                  command, if any |<args>| |                                  command, if any |<args>| | ||||||
|  |                                • fargs: (table) The args split by unescaped | ||||||
|  |                                  whitespace (when more than one argument is | ||||||
|  |                                  allowed), if any |<f-args>| | ||||||
|                                • bang: (boolean) "true" if the command was |                                • bang: (boolean) "true" if the command was | ||||||
|                                  executed with a ! modifier |<bang>| |                                  executed with a ! modifier |<bang>| | ||||||
|                                • line1: (number) The starting line of the |                                • line1: (number) The starting line of the | ||||||
|   | |||||||
| @@ -2415,6 +2415,8 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error * | |||||||
| ///                 from Lua, the command can also be a Lua function. The function is called with a | ///                 from Lua, the command can also be a Lua function. The function is called with a | ||||||
| ///                 single table argument that contains the following keys: | ///                 single table argument that contains the following keys: | ||||||
| ///                 - args: (string) The args passed to the command, if any |<args>| | ///                 - args: (string) The args passed to the command, if any |<args>| | ||||||
|  | ///                 - fargs: (table) The args split by unescaped whitespace (when more than one | ||||||
|  | ///                 argument is allowed), if any |<f-args>| | ||||||
| ///                 - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>| | ///                 - bang: (boolean) "true" if the command was executed with a ! modifier |<bang>| | ||||||
| ///                 - line1: (number) The starting line of the command range |<line1>| | ///                 - line1: (number) The starting line of the command range |<line1>| | ||||||
| ///                 - line2: (number) The final line of the command range |<line2>| | ///                 - line2: (number) The final line of the command range |<line2>| | ||||||
|   | |||||||
| @@ -5802,6 +5802,30 @@ 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 | ||||||
|  | /// @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; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   *end = length - 1; | ||||||
|  |   return false; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* | /* | ||||||
|  * split and quote args for <f-args> |  * split and quote args for <f-args> | ||||||
|  */ |  */ | ||||||
|   | |||||||
| @@ -1814,8 +1814,31 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) | |||||||
|   lua_pushinteger(lstate, eap->line2); |   lua_pushinteger(lstate, eap->line2); | ||||||
|   lua_setfield(lstate, -2, "line2"); |   lua_setfield(lstate, -2, "line2"); | ||||||
|  |  | ||||||
|  |   lua_newtable(lstate);  // f-args table | ||||||
|   lua_pushstring(lstate, (const char *)eap->arg); |   lua_pushstring(lstate, (const char *)eap->arg); | ||||||
|   lua_setfield(lstate, -2, "args"); |   lua_pushvalue(lstate, -1);  // Reference for potential use on f-args | ||||||
|  |   lua_setfield(lstate, -4, "args"); | ||||||
|  |  | ||||||
|  |   // Split args by unescaped whitespace |<f-args>| (nargs dependent) | ||||||
|  |   if (cmd->uc_argt & EX_NOSPC) { | ||||||
|  |     // Commands where nargs = 1 or "?" fargs is the same as args | ||||||
|  |     lua_rawseti(lstate, -2, 1); | ||||||
|  |   } 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; | ||||||
|  |     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++; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   lua_setfield(lstate, -2, "fargs"); | ||||||
|  |  | ||||||
|   lua_pushstring(lstate, (const char *)&eap->regname); |   lua_pushstring(lstate, (const char *)&eap->regname); | ||||||
|   lua_setfield(lstate, -2, "reg"); |   lua_setfield(lstate, -2, "reg"); | ||||||
|   | |||||||
| @@ -107,7 +107,8 @@ describe('nvim_add_user_command', function() | |||||||
|     ]] |     ]] | ||||||
|  |  | ||||||
|     eq({ |     eq({ | ||||||
|       args = "hello", |       args = [[hello my\ friend how\ are\ you?]], | ||||||
|  |       fargs = {[[hello]], [[my\ friend]], [[how\ are\ you?]]}, | ||||||
|       bang = false, |       bang = false, | ||||||
|       line1 = 1, |       line1 = 1, | ||||||
|       line2 = 1, |       line2 = 1, | ||||||
| @@ -115,13 +116,14 @@ describe('nvim_add_user_command', function() | |||||||
|       range = 0, |       range = 0, | ||||||
|       count = 2, |       count = 2, | ||||||
|       reg = "", |       reg = "", | ||||||
|     }, exec_lua [[ |     }, exec_lua [=[ | ||||||
|       vim.api.nvim_command('CommandWithLuaCallback hello') |       vim.api.nvim_command([[CommandWithLuaCallback hello my\ friend how\ are\ you?]]) | ||||||
|       return result |       return result | ||||||
|     ]]) |     ]=]) | ||||||
|  |  | ||||||
|     eq({ |     eq({ | ||||||
|       args = "", |       args = 'h\tey', | ||||||
|  |       fargs = {[[h]], [[ey]]}, | ||||||
|       bang = true, |       bang = true, | ||||||
|       line1 = 10, |       line1 = 10, | ||||||
|       line2 = 10, |       line2 = 10, | ||||||
| @@ -129,13 +131,14 @@ describe('nvim_add_user_command', function() | |||||||
|       range = 1, |       range = 1, | ||||||
|       count = 10, |       count = 10, | ||||||
|       reg = "", |       reg = "", | ||||||
|     }, exec_lua [[ |     }, exec_lua [=[ | ||||||
|       vim.api.nvim_command('botright 10CommandWithLuaCallback!') |       vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey') | ||||||
|       return result |       return result | ||||||
|     ]]) |     ]=]) | ||||||
|  |  | ||||||
|     eq({ |     eq({ | ||||||
|       args = "", |       args = "h", | ||||||
|  |       fargs = {"h"}, | ||||||
|       bang = false, |       bang = false, | ||||||
|       line1 = 1, |       line1 = 1, | ||||||
|       line2 = 42, |       line2 = 42, | ||||||
| @@ -144,9 +147,52 @@ describe('nvim_add_user_command', function() | |||||||
|       count = 42, |       count = 42, | ||||||
|       reg = "", |       reg = "", | ||||||
|     }, exec_lua [[ |     }, exec_lua [[ | ||||||
|       vim.api.nvim_command('CommandWithLuaCallback 42') |       vim.api.nvim_command('CommandWithLuaCallback 42 h') | ||||||
|       return result |       return result | ||||||
|     ]]) |     ]]) | ||||||
|  |  | ||||||
|  |     eq({ | ||||||
|  |       args = "", | ||||||
|  |       fargs = {""},  -- fargs works without args | ||||||
|  |       bang = false, | ||||||
|  |       line1 = 1, | ||||||
|  |       line2 = 1, | ||||||
|  |       mods = "", | ||||||
|  |       range = 0, | ||||||
|  |       count = 2, | ||||||
|  |       reg = "", | ||||||
|  |     }, exec_lua [[ | ||||||
|  |       vim.api.nvim_command('CommandWithLuaCallback') | ||||||
|  |       return result | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|  |     -- f-args doesn't split when command nargs is 1 or "?" | ||||||
|  |     exec_lua [[ | ||||||
|  |       result = {} | ||||||
|  |       vim.api.nvim_add_user_command('CommandWithOneArg', function(opts) | ||||||
|  |         result = opts | ||||||
|  |       end, { | ||||||
|  |         nargs = "?", | ||||||
|  |         bang = true, | ||||||
|  |         count = 2, | ||||||
|  |       }) | ||||||
|  |     ]] | ||||||
|  |  | ||||||
|  |     eq({ | ||||||
|  |       args = "hello I'm one argmuent", | ||||||
|  |       fargs = {"hello I'm one argmuent"},  -- Doesn't split args | ||||||
|  |       bang = false, | ||||||
|  |       line1 = 1, | ||||||
|  |       line2 = 1, | ||||||
|  |       mods = "", | ||||||
|  |       range = 0, | ||||||
|  |       count = 2, | ||||||
|  |       reg = "", | ||||||
|  |     }, exec_lua [[ | ||||||
|  |       vim.api.nvim_command('CommandWithOneArg hello I\'m one argmuent') | ||||||
|  |       return result | ||||||
|  |     ]]) | ||||||
|  |  | ||||||
|   end) |   end) | ||||||
|  |  | ||||||
|   it('can define buffer-local commands', function() |   it('can define buffer-local commands', function() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Javier Lopez
					Javier Lopez