mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
feat(api): implement nvim_{add,del}_user_command
Add support for adding and removing custom user commands with the Nvim API.
This commit is contained in:
parent
9804a2870f
commit
eff11b3c3f
@ -626,6 +626,56 @@ nvim__stats() *nvim__stats()*
|
|||||||
Return: ~
|
Return: ~
|
||||||
Map of various internal stats.
|
Map of various internal stats.
|
||||||
|
|
||||||
|
*nvim_add_user_command()*
|
||||||
|
nvim_add_user_command({name}, {command}, {*opts})
|
||||||
|
Create a new user command |user-commands|
|
||||||
|
|
||||||
|
{name} is the name of the new command. The name must begin
|
||||||
|
with an uppercase letter.
|
||||||
|
|
||||||
|
{command} is the replacement text or Lua function to execute.
|
||||||
|
|
||||||
|
Example: >
|
||||||
|
:call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
|
||||||
|
:SayHello
|
||||||
|
Hello world!
|
||||||
|
<
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{name} Name of the new user command. Must begin with
|
||||||
|
an uppercase letter.
|
||||||
|
{command} Replacement command to execute when this user
|
||||||
|
command is executed. When called 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>|
|
||||||
|
• 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>|
|
||||||
|
• range: (number) The number of items in the
|
||||||
|
command range: 0, 1, or 2 |<range>|
|
||||||
|
• count: (number) Any count supplied |<count>|
|
||||||
|
• reg: (string) The optional register, if
|
||||||
|
specified |<reg>|
|
||||||
|
• mods: (string) Command modifiers, if any
|
||||||
|
|<mods>|
|
||||||
|
{opts} Optional command attributes. See
|
||||||
|
|command-attributes| for more details. To use
|
||||||
|
boolean attributes (such as |:command-bang| or
|
||||||
|
|:command-bar|) set the value to "true". When
|
||||||
|
using a Lua function for {command} you can also
|
||||||
|
provide a "desc" key that will be displayed
|
||||||
|
when listing commands. In addition to the
|
||||||
|
string options listed in |:command-complete|,
|
||||||
|
the "complete" key also accepts a Lua function
|
||||||
|
which works like the "customlist" completion
|
||||||
|
mode |:command-complete-customlist|.
|
||||||
|
|
||||||
nvim_call_atomic({calls}) *nvim_call_atomic()*
|
nvim_call_atomic({calls}) *nvim_call_atomic()*
|
||||||
Calls many API methods atomically.
|
Calls many API methods atomically.
|
||||||
|
|
||||||
@ -714,6 +764,12 @@ nvim_del_mark({name}) *nvim_del_mark()*
|
|||||||
|nvim_buf_del_mark()|
|
|nvim_buf_del_mark()|
|
||||||
|nvim_get_mark()|
|
|nvim_get_mark()|
|
||||||
|
|
||||||
|
nvim_del_user_command({name}) *nvim_del_user_command()*
|
||||||
|
Delete a user-defined command.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{name} Name of the command to delete.
|
||||||
|
|
||||||
nvim_del_var({name}) *nvim_del_var()*
|
nvim_del_var({name}) *nvim_del_var()*
|
||||||
Removes a global (g:) variable.
|
Removes a global (g:) variable.
|
||||||
|
|
||||||
@ -1790,6 +1846,16 @@ nvim__buf_redraw_range({buffer}, {first}, {last})
|
|||||||
nvim__buf_stats({buffer}) *nvim__buf_stats()*
|
nvim__buf_stats({buffer}) *nvim__buf_stats()*
|
||||||
TODO: Documentation
|
TODO: Documentation
|
||||||
|
|
||||||
|
*nvim_buf_add_user_command()*
|
||||||
|
nvim_buf_add_user_command({buffer}, {name}, {command}, {*opts})
|
||||||
|
Create a new user command |user-commands| in the given buffer.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{buffer} Buffer handle, or 0 for current buffer.
|
||||||
|
|
||||||
|
See also: ~
|
||||||
|
nvim_add_user_command
|
||||||
|
|
||||||
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
|
nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
|
||||||
Activates buffer-update events on a channel, or as Lua
|
Activates buffer-update events on a channel, or as Lua
|
||||||
callbacks.
|
callbacks.
|
||||||
@ -1925,6 +1991,18 @@ nvim_buf_del_mark({buffer}, {name}) *nvim_buf_del_mark()*
|
|||||||
|nvim_buf_set_mark()|
|
|nvim_buf_set_mark()|
|
||||||
|nvim_del_mark()|
|
|nvim_del_mark()|
|
||||||
|
|
||||||
|
*nvim_buf_del_user_command()*
|
||||||
|
nvim_buf_del_user_command({buffer}, {name})
|
||||||
|
Delete a buffer-local user-defined command.
|
||||||
|
|
||||||
|
Only commands created with |:command-buffer| or
|
||||||
|
|nvim_buf_add_user_command()| can be deleted with this
|
||||||
|
function.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
{buffer} Buffer handle, or 0 for current buffer.
|
||||||
|
{name} Name of the command to delete.
|
||||||
|
|
||||||
nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
|
nvim_buf_del_var({buffer}, {name}) *nvim_buf_del_var()*
|
||||||
Removes a buffer-scoped (b:) variable
|
Removes a buffer-scoped (b:) variable
|
||||||
|
|
||||||
|
@ -1247,8 +1247,8 @@ See |:verbose-cmd| for more information.
|
|||||||
|
|
||||||
|
|
||||||
Command attributes ~
|
Command attributes ~
|
||||||
|
*command-attributes*
|
||||||
User-defined commands are treated by Vim just like any other Ex commands. They
|
User-defined commands are treated by Nvim just like any other Ex commands. They
|
||||||
can have arguments, or have a range specified. Arguments are subject to
|
can have arguments, or have a range specified. Arguments are subject to
|
||||||
completion as filenames, buffers, etc. Exactly how this works depends upon the
|
completion as filenames, buffers, etc. Exactly how this works depends upon the
|
||||||
command's attributes, which are specified when the command is defined.
|
command's attributes, which are specified when the command is defined.
|
||||||
|
@ -1273,6 +1273,63 @@ Object nvim_buf_call(Buffer buffer, LuaRef fun, Error *err)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new user command |user-commands| in the given buffer.
|
||||||
|
///
|
||||||
|
/// @param buffer Buffer handle, or 0 for current buffer.
|
||||||
|
/// @param[out] err Error details, if any.
|
||||||
|
/// @see nvim_add_user_command
|
||||||
|
void nvim_buf_add_user_command(Buffer buffer, String name, Object command,
|
||||||
|
Dict(user_command) *opts, Error *err)
|
||||||
|
FUNC_API_SINCE(9)
|
||||||
|
{
|
||||||
|
buf_T *target_buf = find_buffer_by_handle(buffer, err);
|
||||||
|
if (ERROR_SET(err)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf_T *save_curbuf = curbuf;
|
||||||
|
curbuf = target_buf;
|
||||||
|
add_user_command(name, command, opts, UC_BUFFER, err);
|
||||||
|
curbuf = save_curbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a buffer-local user-defined command.
|
||||||
|
///
|
||||||
|
/// Only commands created with |:command-buffer| or
|
||||||
|
/// |nvim_buf_add_user_command()| can be deleted with this function.
|
||||||
|
///
|
||||||
|
/// @param buffer Buffer handle, or 0 for current buffer.
|
||||||
|
/// @param name Name of the command to delete.
|
||||||
|
/// @param[out] err Error details, if any.
|
||||||
|
void nvim_buf_del_user_command(Buffer buffer, String name, Error *err)
|
||||||
|
FUNC_API_SINCE(9)
|
||||||
|
{
|
||||||
|
garray_T *gap;
|
||||||
|
if (buffer == -1) {
|
||||||
|
gap = &ucmds;
|
||||||
|
} else {
|
||||||
|
buf_T *buf = find_buffer_by_handle(buffer, err);
|
||||||
|
gap = &buf->b_ucmds;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < gap->ga_len; i++) {
|
||||||
|
ucmd_T *cmd = USER_CMD_GA(gap, i);
|
||||||
|
if (!STRCMP(name.data, cmd->uc_name)) {
|
||||||
|
free_ucmd(cmd);
|
||||||
|
|
||||||
|
gap->ga_len -= 1;
|
||||||
|
|
||||||
|
if (i < gap->ga_len) {
|
||||||
|
memmove(cmd, cmd + 1, (size_t)(gap->ga_len - i) * sizeof(ucmd_T));
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
api_set_error(err, kErrorTypeException, "No such user-defined command: %s", name.data);
|
||||||
|
}
|
||||||
|
|
||||||
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
|
Dictionary nvim__buf_stats(Buffer buffer, Error *err)
|
||||||
{
|
{
|
||||||
Dictionary rv = ARRAY_DICT_INIT;
|
Dictionary rv = ARRAY_DICT_INIT;
|
||||||
|
@ -33,6 +33,18 @@ return {
|
|||||||
get_commands = {
|
get_commands = {
|
||||||
"builtin";
|
"builtin";
|
||||||
};
|
};
|
||||||
|
user_command = {
|
||||||
|
"addr";
|
||||||
|
"bang";
|
||||||
|
"bar";
|
||||||
|
"complete";
|
||||||
|
"count";
|
||||||
|
"desc";
|
||||||
|
"force";
|
||||||
|
"nargs";
|
||||||
|
"range";
|
||||||
|
"register";
|
||||||
|
};
|
||||||
float_config = {
|
float_config = {
|
||||||
"row";
|
"row";
|
||||||
"col";
|
"col";
|
||||||
|
@ -961,6 +961,10 @@ Object copy_object(Object obj)
|
|||||||
|
|
||||||
case kObjectTypeDictionary:
|
case kObjectTypeDictionary:
|
||||||
return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
|
return DICTIONARY_OBJ(copy_dictionary(obj.data.dictionary));
|
||||||
|
|
||||||
|
case kObjectTypeLuaRef:
|
||||||
|
return LUAREF_OBJ(api_new_luaref(obj.data.luaref));
|
||||||
|
|
||||||
default:
|
default:
|
||||||
abort();
|
abort();
|
||||||
}
|
}
|
||||||
@ -1342,3 +1346,184 @@ const char *get_default_stl_hl(win_T *wp)
|
|||||||
return "StatusLineNC";
|
return "StatusLineNC";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void add_user_command(String name, Object command, Dict(user_command) *opts, int flags, Error *err)
|
||||||
|
{
|
||||||
|
uint32_t argt = 0;
|
||||||
|
long def = -1;
|
||||||
|
cmd_addr_T addr_type_arg = ADDR_NONE;
|
||||||
|
int compl = EXPAND_NOTHING;
|
||||||
|
char *compl_arg = NULL;
|
||||||
|
char *rep = NULL;
|
||||||
|
LuaRef luaref = LUA_NOREF;
|
||||||
|
LuaRef compl_luaref = LUA_NOREF;
|
||||||
|
|
||||||
|
if (HAS_KEY(opts->range) && HAS_KEY(opts->count)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "'range' and 'count' are mutually exclusive");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->nargs.type == kObjectTypeInteger) {
|
||||||
|
switch (opts->nargs.data.integer) {
|
||||||
|
case 0:
|
||||||
|
// Default value, nothing to do
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
argt |= EX_EXTRA | EX_NOSPC | EX_NEEDARG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else if (opts->nargs.type == kObjectTypeString) {
|
||||||
|
if (opts->nargs.data.string.size > 1) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (opts->nargs.data.string.data[0]) {
|
||||||
|
case '*':
|
||||||
|
argt |= EX_EXTRA;
|
||||||
|
break;
|
||||||
|
case '?':
|
||||||
|
argt |= EX_EXTRA | EX_NOSPC;
|
||||||
|
break;
|
||||||
|
case '+':
|
||||||
|
argt |= EX_EXTRA | EX_NEEDARG;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else if (HAS_KEY(opts->nargs)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'nargs'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HAS_KEY(opts->complete) && !argt) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "'complete' used without 'nargs'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->range.type == kObjectTypeBoolean) {
|
||||||
|
if (opts->range.data.boolean) {
|
||||||
|
argt |= EX_RANGE;
|
||||||
|
addr_type_arg = ADDR_LINES;
|
||||||
|
}
|
||||||
|
} else if (opts->range.type == kObjectTypeString) {
|
||||||
|
if (opts->range.data.string.data[0] == '%' && opts->range.data.string.size == 1) {
|
||||||
|
argt |= EX_RANGE | EX_DFLALL;
|
||||||
|
addr_type_arg = ADDR_LINES;
|
||||||
|
} else {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else if (opts->range.type == kObjectTypeInteger) {
|
||||||
|
argt |= EX_RANGE | EX_ZEROR;
|
||||||
|
def = opts->range.data.integer;
|
||||||
|
addr_type_arg = ADDR_LINES;
|
||||||
|
} else if (HAS_KEY(opts->range)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'range'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->count.type == kObjectTypeBoolean) {
|
||||||
|
if (opts->count.data.boolean) {
|
||||||
|
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
|
||||||
|
addr_type_arg = ADDR_OTHER;
|
||||||
|
def = 0;
|
||||||
|
}
|
||||||
|
} else if (opts->count.type == kObjectTypeInteger) {
|
||||||
|
argt |= EX_COUNT | EX_ZEROR | EX_RANGE;
|
||||||
|
addr_type_arg = ADDR_OTHER;
|
||||||
|
def = opts->count.data.integer;
|
||||||
|
} else if (HAS_KEY(opts->count)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'count'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->addr.type == kObjectTypeString) {
|
||||||
|
if (parse_addr_type_arg((char_u *)opts->addr.data.string.data, (int)opts->addr.data.string.size,
|
||||||
|
&addr_type_arg) != OK) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addr_type_arg != ADDR_LINES) {
|
||||||
|
argt |= EX_ZEROR;
|
||||||
|
}
|
||||||
|
} else if (HAS_KEY(opts->addr)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'addr'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (api_object_to_bool(opts->bang, "bang", false, err)) {
|
||||||
|
argt |= EX_BANG;
|
||||||
|
} else if (ERROR_SET(err)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (api_object_to_bool(opts->bar, "bar", false, err)) {
|
||||||
|
argt |= EX_TRLBAR;
|
||||||
|
} else if (ERROR_SET(err)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (api_object_to_bool(opts->register_, "register", false, err)) {
|
||||||
|
argt |= EX_REGSTR;
|
||||||
|
} else if (ERROR_SET(err)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool force = api_object_to_bool(opts->force, "force", false, err);
|
||||||
|
if (ERROR_SET(err)) {
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opts->complete.type == kObjectTypeLuaRef) {
|
||||||
|
compl = EXPAND_USER_LUA;
|
||||||
|
compl_luaref = api_new_luaref(opts->complete.data.luaref);
|
||||||
|
} else if (opts->complete.type == kObjectTypeString) {
|
||||||
|
if (parse_compl_arg((char_u *)opts->complete.data.string.data,
|
||||||
|
(int)opts->complete.data.string.size, &compl, &argt,
|
||||||
|
(char_u **)&compl_arg) != OK) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
} else if (HAS_KEY(opts->complete)) {
|
||||||
|
api_set_error(err, kErrorTypeValidation, "Invalid value for 'complete'");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (command.type) {
|
||||||
|
case kObjectTypeLuaRef:
|
||||||
|
luaref = api_new_luaref(command.data.luaref);
|
||||||
|
if (opts->desc.type == kObjectTypeString) {
|
||||||
|
rep = opts->desc.data.string.data;
|
||||||
|
} else {
|
||||||
|
snprintf((char *)IObuff, IOSIZE, "<Lua function %d>", luaref);
|
||||||
|
rep = (char *)IObuff;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case kObjectTypeString:
|
||||||
|
rep = command.data.string.data;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
api_set_error(err, kErrorTypeValidation, "'command' must be a string or Lua function");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uc_add_command((char_u *)name.data, name.size, (char_u *)rep, argt, def, flags,
|
||||||
|
compl, (char_u *)compl_arg, compl_luaref, addr_type_arg, luaref,
|
||||||
|
force) != OK) {
|
||||||
|
api_set_error(err, kErrorTypeException, "Failed to create user command");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
err:
|
||||||
|
NLUA_CLEAR_REF(luaref);
|
||||||
|
NLUA_CLEAR_REF(compl_luaref);
|
||||||
|
}
|
||||||
|
@ -2363,3 +2363,52 @@ Dictionary nvim_eval_statusline(String str, Dict(eval_statusline) *opts, Error *
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new user command |user-commands|
|
||||||
|
///
|
||||||
|
/// {name} is the name of the new command. The name must begin with an uppercase letter.
|
||||||
|
///
|
||||||
|
/// {command} is the replacement text or Lua function to execute.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// <pre>
|
||||||
|
/// :call nvim_add_user_command('SayHello', 'echo "Hello world!"', {})
|
||||||
|
/// :SayHello
|
||||||
|
/// Hello world!
|
||||||
|
/// </pre>
|
||||||
|
///
|
||||||
|
/// @param name Name of the new user command. Must begin with an uppercase letter.
|
||||||
|
/// @param command Replacement command to execute when this user command is executed. When called
|
||||||
|
/// 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>|
|
||||||
|
/// - 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>|
|
||||||
|
/// - range: (number) The number of items in the command range: 0, 1, or 2 |<range>|
|
||||||
|
/// - count: (number) Any count supplied |<count>|
|
||||||
|
/// - reg: (string) The optional register, if specified |<reg>|
|
||||||
|
/// - mods: (string) Command modifiers, if any |<mods>|
|
||||||
|
/// @param opts Optional command attributes. See |command-attributes| for more details. To use
|
||||||
|
/// boolean attributes (such as |:command-bang| or |:command-bar|) set the value to
|
||||||
|
/// "true". When using a Lua function for {command} you can also provide a "desc"
|
||||||
|
/// key that will be displayed when listing commands. In addition to the string
|
||||||
|
/// options listed in |:command-complete|, the "complete" key also accepts a Lua
|
||||||
|
/// function which works like the "customlist" completion mode
|
||||||
|
/// |:command-complete-customlist|.
|
||||||
|
/// @param[out] err Error details, if any.
|
||||||
|
void nvim_add_user_command(String name, Object command, Dict(user_command) *opts, Error *err)
|
||||||
|
FUNC_API_SINCE(9)
|
||||||
|
{
|
||||||
|
add_user_command(name, command, opts, 0, err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a user-defined command.
|
||||||
|
///
|
||||||
|
/// @param name Name of the command to delete.
|
||||||
|
/// @param[out] err Error details, if any.
|
||||||
|
void nvim_del_user_command(String name, Error *err)
|
||||||
|
FUNC_API_SINCE(9)
|
||||||
|
{
|
||||||
|
nvim_buf_del_user_command(-1, name, err);
|
||||||
|
}
|
||||||
|
@ -192,6 +192,7 @@ struct expand {
|
|||||||
int xp_context; // type of expansion
|
int xp_context; // type of expansion
|
||||||
size_t xp_pattern_len; // bytes in xp_pattern before cursor
|
size_t xp_pattern_len; // bytes in xp_pattern before cursor
|
||||||
char_u *xp_arg; // completion function
|
char_u *xp_arg; // completion function
|
||||||
|
LuaRef xp_luaref; // Ref to Lua completion function
|
||||||
sctx_T xp_script_ctx; // SCTX for completion function
|
sctx_T xp_script_ctx; // SCTX for completion function
|
||||||
int xp_backslash; // one of the XP_BS_ values
|
int xp_backslash; // one of the XP_BS_ values
|
||||||
#ifndef BACKSLASH_IN_FILENAME
|
#ifndef BACKSLASH_IN_FILENAME
|
||||||
|
@ -81,23 +81,7 @@
|
|||||||
static int quitmore = 0;
|
static int quitmore = 0;
|
||||||
static bool ex_pressedreturn = false;
|
static bool ex_pressedreturn = false;
|
||||||
|
|
||||||
typedef struct ucmd {
|
garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
|
||||||
char_u *uc_name; // The command name
|
|
||||||
uint32_t uc_argt; // The argument type
|
|
||||||
char_u *uc_rep; // The command's replacement string
|
|
||||||
long uc_def; // The default value for a range/count
|
|
||||||
int uc_compl; // completion type
|
|
||||||
cmd_addr_T uc_addr_type; // The command's address type
|
|
||||||
sctx_T uc_script_ctx; // SCTX where the command was defined
|
|
||||||
char_u *uc_compl_arg; // completion argument if any
|
|
||||||
} ucmd_T;
|
|
||||||
|
|
||||||
#define UC_BUFFER 1 // -buffer: local to current buffer
|
|
||||||
|
|
||||||
static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL };
|
|
||||||
|
|
||||||
#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
|
|
||||||
#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
|
|
||||||
|
|
||||||
// Whether a command index indicates a user command.
|
// Whether a command index indicates a user command.
|
||||||
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
|
#define IS_USER_CMDIDX(idx) ((int)(idx) < 0)
|
||||||
@ -2761,6 +2745,7 @@ static char_u *find_ucmd(exarg_T *eap, char_u *p, int *full, expand_T *xp, int *
|
|||||||
*complp = uc->uc_compl;
|
*complp = uc->uc_compl;
|
||||||
}
|
}
|
||||||
if (xp != NULL) {
|
if (xp != NULL) {
|
||||||
|
xp->xp_luaref = uc->uc_compl_luaref;
|
||||||
xp->xp_arg = uc->uc_compl_arg;
|
xp->xp_arg = uc->uc_compl_arg;
|
||||||
xp->xp_script_ctx = uc->uc_script_ctx;
|
xp->xp_script_ctx = uc->uc_script_ctx;
|
||||||
xp->xp_script_ctx.sc_lnum += sourcing_lnum;
|
xp->xp_script_ctx.sc_lnum += sourcing_lnum;
|
||||||
@ -5171,8 +5156,9 @@ char_u *get_command_name(expand_T *xp, int idx)
|
|||||||
return cmdnames[idx].cmd_name;
|
return cmdnames[idx].cmd_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def,
|
int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t argt, long def, int flags,
|
||||||
int flags, int compl, char_u *compl_arg, cmd_addr_T addr_type, bool force)
|
int compl, char_u *compl_arg, LuaRef compl_luaref, cmd_addr_T addr_type,
|
||||||
|
LuaRef luaref, bool force)
|
||||||
FUNC_ATTR_NONNULL_ARG(1, 3)
|
FUNC_ATTR_NONNULL_ARG(1, 3)
|
||||||
{
|
{
|
||||||
ucmd_T *cmd = NULL;
|
ucmd_T *cmd = NULL;
|
||||||
@ -5226,6 +5212,8 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
|
|||||||
|
|
||||||
XFREE_CLEAR(cmd->uc_rep);
|
XFREE_CLEAR(cmd->uc_rep);
|
||||||
XFREE_CLEAR(cmd->uc_compl_arg);
|
XFREE_CLEAR(cmd->uc_compl_arg);
|
||||||
|
NLUA_CLEAR_REF(cmd->uc_luaref);
|
||||||
|
NLUA_CLEAR_REF(cmd->uc_compl_luaref);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5256,13 +5244,17 @@ static int uc_add_command(char_u *name, size_t name_len, char_u *rep, uint32_t a
|
|||||||
cmd->uc_script_ctx = current_sctx;
|
cmd->uc_script_ctx = current_sctx;
|
||||||
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
|
cmd->uc_script_ctx.sc_lnum += sourcing_lnum;
|
||||||
cmd->uc_compl_arg = compl_arg;
|
cmd->uc_compl_arg = compl_arg;
|
||||||
|
cmd->uc_compl_luaref = compl_luaref;
|
||||||
cmd->uc_addr_type = addr_type;
|
cmd->uc_addr_type = addr_type;
|
||||||
|
cmd->uc_luaref = luaref;
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
xfree(rep_buf);
|
xfree(rep_buf);
|
||||||
xfree(compl_arg);
|
xfree(compl_arg);
|
||||||
|
NLUA_CLEAR_REF(luaref);
|
||||||
|
NLUA_CLEAR_REF(compl_luaref);
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5301,6 +5293,7 @@ static const char *command_complete[] =
|
|||||||
[EXPAND_CSCOPE] = "cscope",
|
[EXPAND_CSCOPE] = "cscope",
|
||||||
[EXPAND_USER_DEFINED] = "custom",
|
[EXPAND_USER_DEFINED] = "custom",
|
||||||
[EXPAND_USER_LIST] = "customlist",
|
[EXPAND_USER_LIST] = "customlist",
|
||||||
|
[EXPAND_USER_LUA] = "<Lua function>",
|
||||||
[EXPAND_DIFF_BUFFERS] = "diff_buffer",
|
[EXPAND_DIFF_BUFFERS] = "diff_buffer",
|
||||||
[EXPAND_DIRECTORIES] = "dir",
|
[EXPAND_DIRECTORIES] = "dir",
|
||||||
[EXPAND_ENV_VARS] = "environment",
|
[EXPAND_ENV_VARS] = "environment",
|
||||||
@ -5702,8 +5695,8 @@ static void ex_command(exarg_T *eap)
|
|||||||
} else if (compl > 0 && (argt & EX_EXTRA) == 0) {
|
} else if (compl > 0 && (argt & EX_EXTRA) == 0) {
|
||||||
emsg(_(e_complete_used_without_nargs));
|
emsg(_(e_complete_used_without_nargs));
|
||||||
} else {
|
} else {
|
||||||
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg,
|
uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, LUA_NOREF,
|
||||||
addr_type_arg, eap->forceit);
|
addr_type_arg, LUA_NOREF, eap->forceit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5717,11 +5710,13 @@ void ex_comclear(exarg_T *eap)
|
|||||||
uc_clear(&curbuf->b_ucmds);
|
uc_clear(&curbuf->b_ucmds);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void free_ucmd(ucmd_T *cmd)
|
void free_ucmd(ucmd_T *cmd)
|
||||||
{
|
{
|
||||||
xfree(cmd->uc_name);
|
xfree(cmd->uc_name);
|
||||||
xfree(cmd->uc_rep);
|
xfree(cmd->uc_rep);
|
||||||
xfree(cmd->uc_compl_arg);
|
xfree(cmd->uc_compl_arg);
|
||||||
|
NLUA_CLEAR_REF(cmd->uc_compl_luaref);
|
||||||
|
NLUA_CLEAR_REF(cmd->uc_luaref);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -5759,9 +5754,7 @@ static void ex_delcommand(exarg_T *eap)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
xfree(cmd->uc_name);
|
free_ucmd(cmd);
|
||||||
xfree(cmd->uc_rep);
|
|
||||||
xfree(cmd->uc_compl_arg);
|
|
||||||
|
|
||||||
--gap->ga_len;
|
--gap->ga_len;
|
||||||
|
|
||||||
@ -5843,7 +5836,7 @@ static char_u *uc_split_args(char_u *arg, size_t *lenp)
|
|||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t add_cmd_modifier(char_u *buf, char *mod_str, bool *multi_mods)
|
static size_t add_cmd_modifier(char *buf, char *mod_str, bool *multi_mods)
|
||||||
{
|
{
|
||||||
size_t result = STRLEN(mod_str);
|
size_t result = STRLEN(mod_str);
|
||||||
if (*multi_mods) {
|
if (*multi_mods) {
|
||||||
@ -6044,70 +6037,8 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
|
|||||||
*buf = '\0';
|
*buf = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
bool multi_mods = false;
|
result += uc_mods((char *)buf);
|
||||||
|
|
||||||
// :aboveleft and :leftabove
|
|
||||||
if (cmdmod.split & WSP_ABOVE) {
|
|
||||||
result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
|
|
||||||
}
|
|
||||||
// :belowright and :rightbelow
|
|
||||||
if (cmdmod.split & WSP_BELOW) {
|
|
||||||
result += add_cmd_modifier(buf, "belowright", &multi_mods);
|
|
||||||
}
|
|
||||||
// :botright
|
|
||||||
if (cmdmod.split & WSP_BOT) {
|
|
||||||
result += add_cmd_modifier(buf, "botright", &multi_mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
bool *set;
|
|
||||||
char *name;
|
|
||||||
} mod_entry_T;
|
|
||||||
static mod_entry_T mod_entries[] = {
|
|
||||||
{ &cmdmod.browse, "browse" },
|
|
||||||
{ &cmdmod.confirm, "confirm" },
|
|
||||||
{ &cmdmod.hide, "hide" },
|
|
||||||
{ &cmdmod.keepalt, "keepalt" },
|
|
||||||
{ &cmdmod.keepjumps, "keepjumps" },
|
|
||||||
{ &cmdmod.keepmarks, "keepmarks" },
|
|
||||||
{ &cmdmod.keeppatterns, "keeppatterns" },
|
|
||||||
{ &cmdmod.lockmarks, "lockmarks" },
|
|
||||||
{ &cmdmod.noswapfile, "noswapfile" }
|
|
||||||
};
|
|
||||||
// the modifiers that are simple flags
|
|
||||||
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
|
|
||||||
if (*mod_entries[i].set) {
|
|
||||||
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vim): How to support :noautocmd?
|
|
||||||
// TODO(vim): How to support :sandbox?
|
|
||||||
|
|
||||||
// :silent
|
|
||||||
if (msg_silent > 0) {
|
|
||||||
result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent",
|
|
||||||
&multi_mods);
|
|
||||||
}
|
|
||||||
// :tab
|
|
||||||
if (cmdmod.tab > 0) {
|
|
||||||
result += add_cmd_modifier(buf, "tab", &multi_mods);
|
|
||||||
}
|
|
||||||
// :topleft
|
|
||||||
if (cmdmod.split & WSP_TOP) {
|
|
||||||
result += add_cmd_modifier(buf, "topleft", &multi_mods);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(vim): How to support :unsilent?
|
|
||||||
|
|
||||||
// :verbose
|
|
||||||
if (p_verbose > 0) {
|
|
||||||
result += add_cmd_modifier(buf, "verbose", &multi_mods);
|
|
||||||
}
|
|
||||||
// :vertical
|
|
||||||
if (cmdmod.split & WSP_VERT) {
|
|
||||||
result += add_cmd_modifier(buf, "vertical", &multi_mods);
|
|
||||||
}
|
|
||||||
if (quote && buf != NULL) {
|
if (quote && buf != NULL) {
|
||||||
buf += result - 2;
|
buf += result - 2;
|
||||||
*buf = '"';
|
*buf = '"';
|
||||||
@ -6152,6 +6083,76 @@ static size_t uc_check_code(char_u *code, size_t len, char_u *buf, ucmd_T *cmd,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t uc_mods(char *buf)
|
||||||
|
{
|
||||||
|
size_t result = 0;
|
||||||
|
bool multi_mods = false;
|
||||||
|
|
||||||
|
// :aboveleft and :leftabove
|
||||||
|
if (cmdmod.split & WSP_ABOVE) {
|
||||||
|
result += add_cmd_modifier(buf, "aboveleft", &multi_mods);
|
||||||
|
}
|
||||||
|
// :belowright and :rightbelow
|
||||||
|
if (cmdmod.split & WSP_BELOW) {
|
||||||
|
result += add_cmd_modifier(buf, "belowright", &multi_mods);
|
||||||
|
}
|
||||||
|
// :botright
|
||||||
|
if (cmdmod.split & WSP_BOT) {
|
||||||
|
result += add_cmd_modifier(buf, "botright", &multi_mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool *set;
|
||||||
|
char *name;
|
||||||
|
} mod_entry_T;
|
||||||
|
static mod_entry_T mod_entries[] = {
|
||||||
|
{ &cmdmod.browse, "browse" },
|
||||||
|
{ &cmdmod.confirm, "confirm" },
|
||||||
|
{ &cmdmod.hide, "hide" },
|
||||||
|
{ &cmdmod.keepalt, "keepalt" },
|
||||||
|
{ &cmdmod.keepjumps, "keepjumps" },
|
||||||
|
{ &cmdmod.keepmarks, "keepmarks" },
|
||||||
|
{ &cmdmod.keeppatterns, "keeppatterns" },
|
||||||
|
{ &cmdmod.lockmarks, "lockmarks" },
|
||||||
|
{ &cmdmod.noswapfile, "noswapfile" }
|
||||||
|
};
|
||||||
|
// the modifiers that are simple flags
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(mod_entries); i++) {
|
||||||
|
if (*mod_entries[i].set) {
|
||||||
|
result += add_cmd_modifier(buf, mod_entries[i].name, &multi_mods);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vim): How to support :noautocmd?
|
||||||
|
// TODO(vim): How to support :sandbox?
|
||||||
|
|
||||||
|
// :silent
|
||||||
|
if (msg_silent > 0) {
|
||||||
|
result += add_cmd_modifier(buf, emsg_silent > 0 ? "silent!" : "silent", &multi_mods);
|
||||||
|
}
|
||||||
|
// :tab
|
||||||
|
if (cmdmod.tab > 0) {
|
||||||
|
result += add_cmd_modifier(buf, "tab", &multi_mods);
|
||||||
|
}
|
||||||
|
// :topleft
|
||||||
|
if (cmdmod.split & WSP_TOP) {
|
||||||
|
result += add_cmd_modifier(buf, "topleft", &multi_mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(vim): How to support :unsilent?
|
||||||
|
|
||||||
|
// :verbose
|
||||||
|
if (p_verbose > 0) {
|
||||||
|
result += add_cmd_modifier(buf, "verbose", &multi_mods);
|
||||||
|
}
|
||||||
|
// :vertical
|
||||||
|
if (cmdmod.split & WSP_VERT) {
|
||||||
|
result += add_cmd_modifier(buf, "vertical", &multi_mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
static void do_ucmd(exarg_T *eap)
|
static void do_ucmd(exarg_T *eap)
|
||||||
{
|
{
|
||||||
char_u *buf;
|
char_u *buf;
|
||||||
@ -6174,6 +6175,11 @@ static void do_ucmd(exarg_T *eap)
|
|||||||
cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
|
cmd = USER_CMD_GA(&curbuf->b_ucmds, eap->useridx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cmd->uc_luaref > 0) {
|
||||||
|
nlua_do_ucmd(cmd, eap);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Replace <> in the command by the arguments.
|
* Replace <> in the command by the arguments.
|
||||||
* First round: "buf" is NULL, compute length, allocate "buf".
|
* First round: "buf" is NULL, compute length, allocate "buf".
|
||||||
|
@ -32,6 +32,26 @@ typedef struct {
|
|||||||
tasave_T tabuf;
|
tasave_T tabuf;
|
||||||
} save_state_T;
|
} save_state_T;
|
||||||
|
|
||||||
|
typedef struct ucmd {
|
||||||
|
char_u *uc_name; // The command name
|
||||||
|
uint32_t uc_argt; // The argument type
|
||||||
|
char_u *uc_rep; // The command's replacement string
|
||||||
|
long uc_def; // The default value for a range/count
|
||||||
|
int uc_compl; // completion type
|
||||||
|
cmd_addr_T uc_addr_type; // The command's address type
|
||||||
|
sctx_T uc_script_ctx; // SCTX where the command was defined
|
||||||
|
char_u *uc_compl_arg; // completion argument if any
|
||||||
|
LuaRef uc_compl_luaref; // Reference to Lua completion function
|
||||||
|
LuaRef uc_luaref; // Reference to Lua function
|
||||||
|
} ucmd_T;
|
||||||
|
|
||||||
|
#define UC_BUFFER 1 // -buffer: local to current buffer
|
||||||
|
|
||||||
|
extern garray_T ucmds;
|
||||||
|
|
||||||
|
#define USER_CMD(i) (&((ucmd_T *)(ucmds.ga_data))[i])
|
||||||
|
#define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i])
|
||||||
|
|
||||||
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "ex_docmd.h.generated.h"
|
# include "ex_docmd.h.generated.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -4983,6 +4983,9 @@ static int ExpandFromContext(expand_T *xp, char_u *pat, int *num_file, char_u **
|
|||||||
if (xp->xp_context == EXPAND_USER_LIST) {
|
if (xp->xp_context == EXPAND_USER_LIST) {
|
||||||
return ExpandUserList(xp, num_file, file);
|
return ExpandUserList(xp, num_file, file);
|
||||||
}
|
}
|
||||||
|
if (xp->xp_context == EXPAND_USER_LUA) {
|
||||||
|
return ExpandUserLua(xp, num_file, file);
|
||||||
|
}
|
||||||
if (xp->xp_context == EXPAND_PACKADD) {
|
if (xp->xp_context == EXPAND_PACKADD) {
|
||||||
return ExpandPackAddDir(pat, num_file, file);
|
return ExpandPackAddDir(pat, num_file, file);
|
||||||
}
|
}
|
||||||
@ -5411,6 +5414,35 @@ static int ExpandUserList(expand_T *xp, int *num_file, char_u ***file)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ExpandUserLua(expand_T *xp, int *num_file, char_u ***file)
|
||||||
|
{
|
||||||
|
typval_T rettv;
|
||||||
|
nlua_call_user_expand_func(xp, &rettv);
|
||||||
|
if (rettv.v_type != VAR_LIST) {
|
||||||
|
tv_clear(&rettv);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_T *const retlist = rettv.vval.v_list;
|
||||||
|
|
||||||
|
garray_T ga;
|
||||||
|
ga_init(&ga, (int)sizeof(char *), 3);
|
||||||
|
// Loop over the items in the list.
|
||||||
|
TV_LIST_ITER_CONST(retlist, li, {
|
||||||
|
if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING
|
||||||
|
|| TV_LIST_ITEM_TV(li)->vval.v_string == NULL) {
|
||||||
|
continue; // Skip non-string items and empty strings.
|
||||||
|
}
|
||||||
|
|
||||||
|
GA_APPEND(char *, &ga, xstrdup((const char *)TV_LIST_ITEM_TV(li)->vval.v_string));
|
||||||
|
});
|
||||||
|
tv_list_unref(retlist);
|
||||||
|
|
||||||
|
*file = ga.ga_data;
|
||||||
|
*num_file = ga.ga_len;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/// Expand color scheme, compiler or filetype names.
|
/// Expand color scheme, compiler or filetype names.
|
||||||
/// Search from 'runtimepath':
|
/// Search from 'runtimepath':
|
||||||
/// 'runtimepath'/{dirnames}/{pat}.vim
|
/// 'runtimepath'/{dirnames}/{pat}.vim
|
||||||
|
@ -441,8 +441,8 @@ local function process_function(fn)
|
|||||||
local cparam = string.format('arg%u', j)
|
local cparam = string.format('arg%u', j)
|
||||||
local param_type = real_type(param[1])
|
local param_type = real_type(param[1])
|
||||||
local lc_param_type = real_type(param[1]):lower()
|
local lc_param_type = real_type(param[1]):lower()
|
||||||
local extra = ((param_type == "Object" or param_type == "Dictionary") and "false, ") or ""
|
local extra = param_type == "Dictionary" and "false, " or ""
|
||||||
if param[1] == "DictionaryOf(LuaRef)" then
|
if param[1] == "Object" or param[1] == "DictionaryOf(LuaRef)" then
|
||||||
extra = "true, "
|
extra = "true, "
|
||||||
end
|
end
|
||||||
local errshift = 0
|
local errshift = 0
|
||||||
|
@ -26,6 +26,17 @@ local defspipe = io.open(defs_file, 'wb')
|
|||||||
|
|
||||||
local keysets = require'api.keysets'
|
local keysets = require'api.keysets'
|
||||||
|
|
||||||
|
local keywords = {
|
||||||
|
register = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function sanitize(key)
|
||||||
|
if keywords[key] then
|
||||||
|
return key .. "_"
|
||||||
|
end
|
||||||
|
return key
|
||||||
|
end
|
||||||
|
|
||||||
for name, keys in pairs(keysets) do
|
for name, keys in pairs(keysets) do
|
||||||
local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
|
local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx)
|
||||||
return name.."_table["..idx.."].str"
|
return name.."_table["..idx.."].str"
|
||||||
@ -33,7 +44,7 @@ for name, keys in pairs(keysets) do
|
|||||||
|
|
||||||
defspipe:write("typedef struct {\n")
|
defspipe:write("typedef struct {\n")
|
||||||
for _, key in ipairs(neworder) do
|
for _, key in ipairs(neworder) do
|
||||||
defspipe:write(" Object "..key..";\n")
|
defspipe:write(" Object "..sanitize(key)..";\n")
|
||||||
end
|
end
|
||||||
defspipe:write("} KeyDict_"..name..";\n\n")
|
defspipe:write("} KeyDict_"..name..";\n\n")
|
||||||
|
|
||||||
@ -41,7 +52,7 @@ for name, keys in pairs(keysets) do
|
|||||||
|
|
||||||
funcspipe:write("KeySetLink "..name.."_table[] = {\n")
|
funcspipe:write("KeySetLink "..name.."_table[] = {\n")
|
||||||
for _, key in ipairs(neworder) do
|
for _, key in ipairs(neworder) do
|
||||||
funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..key..")},\n")
|
funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..sanitize(key)..")},\n")
|
||||||
end
|
end
|
||||||
funcspipe:write(' {NULL, 0},\n')
|
funcspipe:write(' {NULL, 0},\n')
|
||||||
funcspipe:write("};\n\n")
|
funcspipe:write("};\n\n")
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/event/time.h"
|
#include "nvim/event/time.h"
|
||||||
#include "nvim/ex_cmds2.h"
|
#include "nvim/ex_cmds2.h"
|
||||||
|
#include "nvim/ex_docmd.h"
|
||||||
#include "nvim/ex_getln.h"
|
#include "nvim/ex_getln.h"
|
||||||
#include "nvim/extmark.h"
|
#include "nvim/extmark.h"
|
||||||
#include "nvim/func_attr.h"
|
#include "nvim/func_attr.h"
|
||||||
@ -914,6 +915,24 @@ void nlua_typval_call(const char *str, size_t len, typval_T *const args, int arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nlua_call_user_expand_func(expand_T *xp, typval_T *ret_tv)
|
||||||
|
FUNC_ATTR_NONNULL_ALL
|
||||||
|
{
|
||||||
|
lua_State *const lstate = global_lstate;
|
||||||
|
|
||||||
|
nlua_pushref(lstate, xp->xp_luaref);
|
||||||
|
lua_pushstring(lstate, (char *)xp->xp_pattern);
|
||||||
|
lua_pushstring(lstate, (char *)xp->xp_line);
|
||||||
|
lua_pushinteger(lstate, xp->xp_col);
|
||||||
|
|
||||||
|
if (nlua_pcall(lstate, 3, 1)) {
|
||||||
|
nlua_error(lstate, _("E5108: Error executing Lua function: %.*s"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlua_pop_typval(lstate, ret_tv);
|
||||||
|
}
|
||||||
|
|
||||||
static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name,
|
static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name,
|
||||||
typval_T *const args, int argcount, bool special, typval_T *ret_tv)
|
typval_T *const args, int argcount, bool special, typval_T *ret_tv)
|
||||||
{
|
{
|
||||||
@ -1432,3 +1451,48 @@ void nlua_execute_on_key(int c)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void nlua_do_ucmd(ucmd_T *cmd, exarg_T *eap)
|
||||||
|
{
|
||||||
|
lua_State *const lstate = global_lstate;
|
||||||
|
|
||||||
|
nlua_pushref(lstate, cmd->uc_luaref);
|
||||||
|
|
||||||
|
lua_newtable(lstate);
|
||||||
|
lua_pushboolean(lstate, eap->forceit == 1);
|
||||||
|
lua_setfield(lstate, -2, "bang");
|
||||||
|
|
||||||
|
lua_pushinteger(lstate, eap->line1);
|
||||||
|
lua_setfield(lstate, -2, "line1");
|
||||||
|
|
||||||
|
lua_pushinteger(lstate, eap->line2);
|
||||||
|
lua_setfield(lstate, -2, "line2");
|
||||||
|
|
||||||
|
lua_pushstring(lstate, (const char *)eap->arg);
|
||||||
|
lua_setfield(lstate, -2, "args");
|
||||||
|
|
||||||
|
lua_pushstring(lstate, (const char *)&eap->regname);
|
||||||
|
lua_setfield(lstate, -2, "reg");
|
||||||
|
|
||||||
|
lua_pushinteger(lstate, eap->addr_count);
|
||||||
|
lua_setfield(lstate, -2, "range");
|
||||||
|
|
||||||
|
if (eap->addr_count > 0) {
|
||||||
|
lua_pushinteger(lstate, eap->line2);
|
||||||
|
} else {
|
||||||
|
lua_pushinteger(lstate, cmd->uc_def);
|
||||||
|
}
|
||||||
|
lua_setfield(lstate, -2, "count");
|
||||||
|
|
||||||
|
// The size of this buffer is chosen empirically to be large enough to hold
|
||||||
|
// every possible modifier (with room to spare). If the list of possible
|
||||||
|
// modifiers grows this may need to be updated.
|
||||||
|
char buf[200] = { 0 };
|
||||||
|
(void)uc_mods(buf);
|
||||||
|
lua_pushstring(lstate, buf);
|
||||||
|
lua_setfield(lstate, -2, "mods");
|
||||||
|
|
||||||
|
if (nlua_pcall(lstate, 1, 0)) {
|
||||||
|
nlua_error(lstate, _("Error executing Lua callback: %.*s"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "nvim/api/private/defs.h"
|
#include "nvim/api/private/defs.h"
|
||||||
#include "nvim/eval/typval.h"
|
#include "nvim/eval/typval.h"
|
||||||
#include "nvim/ex_cmds_defs.h"
|
#include "nvim/ex_cmds_defs.h"
|
||||||
|
#include "nvim/ex_docmd.h"
|
||||||
#include "nvim/func_attr.h"
|
#include "nvim/func_attr.h"
|
||||||
#include "nvim/lua/converter.h"
|
#include "nvim/lua/converter.h"
|
||||||
|
|
||||||
|
@ -143,6 +143,7 @@ enum {
|
|||||||
EXPAND_COMPILER,
|
EXPAND_COMPILER,
|
||||||
EXPAND_USER_DEFINED,
|
EXPAND_USER_DEFINED,
|
||||||
EXPAND_USER_LIST,
|
EXPAND_USER_LIST,
|
||||||
|
EXPAND_USER_LUA,
|
||||||
EXPAND_SHELLCMD,
|
EXPAND_SHELLCMD,
|
||||||
EXPAND_CSCOPE,
|
EXPAND_CSCOPE,
|
||||||
EXPAND_SIGN,
|
EXPAND_SIGN,
|
||||||
|
@ -6,8 +6,14 @@ local command = helpers.command
|
|||||||
local curbufmeths = helpers.curbufmeths
|
local curbufmeths = helpers.curbufmeths
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
local meths = helpers.meths
|
local meths = helpers.meths
|
||||||
|
local bufmeths = helpers.bufmeths
|
||||||
|
local matches = helpers.matches
|
||||||
local source = helpers.source
|
local source = helpers.source
|
||||||
local pcall_err = helpers.pcall_err
|
local pcall_err = helpers.pcall_err
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
local assert_alive = helpers.assert_alive
|
||||||
|
local feed = helpers.feed
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
|
||||||
describe('nvim_get_commands', function()
|
describe('nvim_get_commands', function()
|
||||||
local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
|
local cmd_dict = { addr=NIL, bang=false, bar=false, complete=NIL, complete_arg=NIL, count=NIL, definition='echo "Hello World"', name='Hello', nargs='1', range=NIL, register=false, script_id=0, }
|
||||||
@ -78,3 +84,118 @@ describe('nvim_get_commands', function()
|
|||||||
eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
|
eq({Cmd2=cmd2, Cmd3=cmd3, Cmd4=cmd4, Finger=cmd1, TestCmd=cmd0}, meths.get_commands({builtin=false}))
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
describe('nvim_add_user_command', function()
|
||||||
|
before_each(clear)
|
||||||
|
|
||||||
|
it('works with strings', function()
|
||||||
|
meths.add_user_command('SomeCommand', 'let g:command_fired = <args>', {nargs = 1})
|
||||||
|
meths.command('SomeCommand 42')
|
||||||
|
eq(42, meths.eval('g:command_fired'))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works with Lua functions', function()
|
||||||
|
exec_lua [[
|
||||||
|
result = {}
|
||||||
|
vim.api.nvim_add_user_command('CommandWithLuaCallback', function(opts)
|
||||||
|
result = opts
|
||||||
|
end, {
|
||||||
|
nargs = "*",
|
||||||
|
bang = true,
|
||||||
|
count = 2,
|
||||||
|
})
|
||||||
|
]]
|
||||||
|
|
||||||
|
eq({
|
||||||
|
args = "hello",
|
||||||
|
bang = false,
|
||||||
|
line1 = 1,
|
||||||
|
line2 = 1,
|
||||||
|
mods = "",
|
||||||
|
range = 0,
|
||||||
|
count = 2,
|
||||||
|
reg = "",
|
||||||
|
}, exec_lua [[
|
||||||
|
vim.api.nvim_command('CommandWithLuaCallback hello')
|
||||||
|
return result
|
||||||
|
]])
|
||||||
|
|
||||||
|
eq({
|
||||||
|
args = "",
|
||||||
|
bang = true,
|
||||||
|
line1 = 10,
|
||||||
|
line2 = 10,
|
||||||
|
mods = "botright",
|
||||||
|
range = 1,
|
||||||
|
count = 10,
|
||||||
|
reg = "",
|
||||||
|
}, exec_lua [[
|
||||||
|
vim.api.nvim_command('botright 10CommandWithLuaCallback!')
|
||||||
|
return result
|
||||||
|
]])
|
||||||
|
|
||||||
|
eq({
|
||||||
|
args = "",
|
||||||
|
bang = false,
|
||||||
|
line1 = 1,
|
||||||
|
line2 = 42,
|
||||||
|
mods = "",
|
||||||
|
range = 1,
|
||||||
|
count = 42,
|
||||||
|
reg = "",
|
||||||
|
}, exec_lua [[
|
||||||
|
vim.api.nvim_command('CommandWithLuaCallback 42')
|
||||||
|
return result
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can define buffer-local commands', function()
|
||||||
|
local bufnr = meths.create_buf(false, false)
|
||||||
|
bufmeths.add_user_command(bufnr, "Hello", "", {})
|
||||||
|
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
|
||||||
|
meths.set_current_buf(bufnr)
|
||||||
|
meths.command("Hello")
|
||||||
|
assert_alive()
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can use a Lua complete function', function()
|
||||||
|
exec_lua [[
|
||||||
|
vim.api.nvim_add_user_command('Test', '', {
|
||||||
|
nargs = "*",
|
||||||
|
complete = function(arg, cmdline, pos)
|
||||||
|
local options = {"aaa", "bbb", "ccc"}
|
||||||
|
local t = {}
|
||||||
|
for _, v in ipairs(options) do
|
||||||
|
if string.find(v, "^" .. arg) then
|
||||||
|
table.insert(t, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end,
|
||||||
|
})
|
||||||
|
]]
|
||||||
|
|
||||||
|
feed(':Test a<Tab>')
|
||||||
|
eq('Test aaa', funcs.getcmdline())
|
||||||
|
feed('<C-U>Test b<Tab>')
|
||||||
|
eq('Test bbb', funcs.getcmdline())
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
||||||
|
describe('nvim_del_user_command', function()
|
||||||
|
before_each(clear)
|
||||||
|
|
||||||
|
it('can delete global commands', function()
|
||||||
|
meths.add_user_command('Hello', 'echo "Hi"', {})
|
||||||
|
meths.command('Hello')
|
||||||
|
meths.del_user_command('Hello')
|
||||||
|
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can delete buffer-local commands', function()
|
||||||
|
bufmeths.add_user_command(0, 'Hello', 'echo "Hi"', {})
|
||||||
|
meths.command('Hello')
|
||||||
|
bufmeths.del_user_command(0, 'Hello')
|
||||||
|
matches("Not an editor command: Hello", pcall_err(meths.command, "Hello"))
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
@ -8,6 +8,7 @@ local clear = helpers.clear
|
|||||||
local eval = helpers.eval
|
local eval = helpers.eval
|
||||||
local NIL = helpers.NIL
|
local NIL = helpers.NIL
|
||||||
local eq = helpers.eq
|
local eq = helpers.eq
|
||||||
|
local exec_lua = helpers.exec_lua
|
||||||
|
|
||||||
before_each(clear)
|
before_each(clear)
|
||||||
|
|
||||||
@ -111,6 +112,12 @@ describe('luaeval(vim.api.…)', function()
|
|||||||
eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]]))
|
eq(7, eval([[type(luaeval('vim.api.nvim__id(nil)'))]]))
|
||||||
|
|
||||||
eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})'))
|
eq({foo=1, bar={42, {{baz=true}, 5}}}, funcs.luaeval('vim.api.nvim__id({foo=1, bar={42, {{baz=true}, 5}}})'))
|
||||||
|
|
||||||
|
eq(true, funcs.luaeval('vim.api.nvim__id(vim.api.nvim__id)(true)'))
|
||||||
|
eq(42, exec_lua [[
|
||||||
|
local f = vim.api.nvim__id({42, vim.api.nvim__id})
|
||||||
|
return f[2](f[1])
|
||||||
|
]])
|
||||||
end)
|
end)
|
||||||
|
|
||||||
it('correctly converts container objects with type_idx to API objects', function()
|
it('correctly converts container objects with type_idx to API objects', function()
|
||||||
@ -159,12 +166,8 @@ describe('luaeval(vim.api.…)', function()
|
|||||||
|
|
||||||
it('errors out correctly when working with API', function()
|
it('errors out correctly when working with API', function()
|
||||||
-- Conversion errors
|
-- Conversion errors
|
||||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
|
|
||||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id(vim.api.nvim__id)")]])))
|
|
||||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
|
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua table',
|
||||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
|
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({1, foo=42})")]])))
|
||||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Cannot convert given lua type',
|
|
||||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id({42, vim.api.nvim__id})")]])))
|
|
||||||
-- Errors in number of arguments
|
-- Errors in number of arguments
|
||||||
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
|
eq('Vim(call):E5108: Error executing lua [string "luaeval()"]:1: Expected 1 argument',
|
||||||
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id()")]])))
|
remove_trace(exc_exec([[call luaeval("vim.api.nvim__id()")]])))
|
||||||
|
Loading…
Reference in New Issue
Block a user