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: | ||||
|                                • 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>| | ||||
|                                • 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 | ||||
| ///                 single table argument that contains the following keys: | ||||
| ///                 - 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>| | ||||
| ///                 - line1: (number) The starting line of the command range |<line1>| | ||||
| ///                 - 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> | ||||
|  */ | ||||
|   | ||||
| @@ -1814,8 +1814,31 @@ void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap) | ||||
|   lua_pushinteger(lstate, eap->line2); | ||||
|   lua_setfield(lstate, -2, "line2"); | ||||
|  | ||||
|   lua_newtable(lstate);  // f-args table | ||||
|   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_setfield(lstate, -2, "reg"); | ||||
|   | ||||
| @@ -107,7 +107,8 @@ describe('nvim_add_user_command', function() | ||||
|     ]] | ||||
|  | ||||
|     eq({ | ||||
|       args = "hello", | ||||
|       args = [[hello my\ friend how\ are\ you?]], | ||||
|       fargs = {[[hello]], [[my\ friend]], [[how\ are\ you?]]}, | ||||
|       bang = false, | ||||
|       line1 = 1, | ||||
|       line2 = 1, | ||||
| @@ -115,13 +116,14 @@ describe('nvim_add_user_command', function() | ||||
|       range = 0, | ||||
|       count = 2, | ||||
|       reg = "", | ||||
|     }, exec_lua [[ | ||||
|       vim.api.nvim_command('CommandWithLuaCallback hello') | ||||
|     }, exec_lua [=[ | ||||
|       vim.api.nvim_command([[CommandWithLuaCallback hello my\ friend how\ are\ you?]]) | ||||
|       return result | ||||
|     ]]) | ||||
|     ]=]) | ||||
|  | ||||
|     eq({ | ||||
|       args = "", | ||||
|       args = 'h\tey', | ||||
|       fargs = {[[h]], [[ey]]}, | ||||
|       bang = true, | ||||
|       line1 = 10, | ||||
|       line2 = 10, | ||||
| @@ -129,13 +131,14 @@ describe('nvim_add_user_command', function() | ||||
|       range = 1, | ||||
|       count = 10, | ||||
|       reg = "", | ||||
|     }, exec_lua [[ | ||||
|       vim.api.nvim_command('botright 10CommandWithLuaCallback!') | ||||
|     }, exec_lua [=[ | ||||
|       vim.api.nvim_command('botright 10CommandWithLuaCallback! h\tey') | ||||
|       return result | ||||
|     ]]) | ||||
|     ]=]) | ||||
|  | ||||
|     eq({ | ||||
|       args = "", | ||||
|       args = "h", | ||||
|       fargs = {"h"}, | ||||
|       bang = false, | ||||
|       line1 = 1, | ||||
|       line2 = 42, | ||||
| @@ -144,9 +147,52 @@ describe('nvim_add_user_command', function() | ||||
|       count = 42, | ||||
|       reg = "", | ||||
|     }, exec_lua [[ | ||||
|       vim.api.nvim_command('CommandWithLuaCallback 42') | ||||
|       vim.api.nvim_command('CommandWithLuaCallback 42 h') | ||||
|       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) | ||||
|  | ||||
|   it('can define buffer-local commands', function() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Javier Lopez
					Javier Lopez